Documentazione API — GTG-SEOseo.gtechgroup.itv1

GTG-SEO — Documentazione API REST

Prodotto: Suite SEO GTG (SEO G Tech Group) · Versione API: v1 Base URL: https://seo.gtechgroup.it/api/seo (alias versionato: https://seo.gtechgroup.it/api/v1/seo) Autenticazione: Authorization: Bearer <API_KEY> Formato: JSON (UTF-8) · Aggiornato: 2026-06-30


Questa è la documentazione completa e ufficiale dell'API REST della suite GTG-SEO. Espone via API tutte le funzioni del prodotto: SEO Spider (crawl tecnico), GEO / AI Search (GEO audit, AI Visibility, AI Engine, AI Competitor, AI Prompt Research), Rank Tracker (progetti e keyword), Keyword Research, Google Search Console, Google Analytics e Alert.

Tutte le richieste richiedono un'API key valida (vedi Authentication) e sono soggette a rate-limit. Le risposte sono incapsulate in un oggetto {"data": ...}; gli elenchi includono un blocco meta di paginazione.


Indice

  1. Introduzione e convenzioni
  2. Base URL
  3. Autenticazione
  4. Rate limiting
  5. Convenzioni (paginazione, CSV, wrapping)
  6. Codici di errore standard
  7. GET /status
  8. GET / — Discovery
  9. Rank Tracker — Progetti, Keyword, Keyword Research
  10. SEO Spider
  11. GEO & AI Search
  12. Search Console, Analytics & Alerts


Introduction

The GTG-SEO REST API exposes the GTG-SEO suite over HTTP/JSON. A single controller (GtgSeoApi) sub-routes on the path segments after seo, giving programmatic access to:

The discovery index (GET /) returns "name": "SEO API", "version": "v1".


Base URL

https://seo.gtechgroup.it/api/seo

A versioned alias is also registered and served by the same controller:

https://seo.gtechgroup.it/api/v1/seo

Both routes (seo and v1) are mapped to GtgSeoApi. When the request comes in under /api/v1/seo/..., the controller drops the leading seo version-prefix segment, so the two forms are equivalent (the literal seo is never treated as a resource name).

Resource names that contain multiple words use dashes in the URL (e.g. search-console, ai-visibility, keyword-research). The router normalizes dashes to underscores internally and the controller restores them, so dashed paths are the canonical form.


Authentication

All requests authenticate with a Bearer API key in the Authorization header. No cookies are used (CORS is enabled with Access-Control-Allow-Origin: *).

Authorization: Bearer <api_key>

The api_key comes from the user account. Two conditions must hold:

  1. The API must be enabled globally by the administrator.
  2. The user's plan must have the API enabled (plan_settings.api_is_enabled).

The API key is matched against an active user account (users.status = 1).

Auth example

curl -s https://seo.gtechgroup.it/api/seo/status \
  -H "Authorization: Bearer YOUR_API_KEY"

Missing or invalid token

When the Authorization: Bearer header is missing, the API responds 401 with the "no bearer" error. When the token is present but does not match an active account (or the account's plan does not have the API enabled), the API responds 401 with the "no access" error. The error body follows the standard error envelope (see Conventions):

{
  "errors": [
    { "title": "...", "status": "401" }
  ]
}

If the API is globally disabled by the administrator, the request resolves to a 404 (the route is hidden) rather than a 401.


Rate limiting

Non-admin accounts are limited to 60 requests per 60 seconds per API key (a fixed-window counter keyed on the API key). Each response carries rate-limit headers:

Header Meaning
X-RateLimit-Limit The window limit (60).
X-RateLimit-Remaining Requests remaining in the current window (omitted once negative).
X-RateLimit-Reset Seconds until the window resets (sent when the limit is exceeded).

When the limit is exceeded the API responds 429 (rate-limit error). Admin accounts (type = 1) are not rate-limited.

Some write/analysis endpoints enforce additional per-hour quotas (e.g. 30/hour for GEO, AI Engine, AI Visibility, AI Competitor and prompt research; 60/hour for keyword research). Hitting those quotas also returns 429.


Conventions

Response envelope

Successful responses are JSON wrapped in a top-level data key:

{ "data": ... }

List endpoints add a meta object:

{
  "data": [ ... ],
  "meta": {
    "page": 1,
    "per_page": 25,
    "total_results": 137,
    "total_pages": 6
  }
}

Error responses use a top-level errors array, each entry with title and status:

{
  "errors": [
    { "title": "Risorsa non trovata.", "status": "404" }
  ]
}

Pagination

List endpoints accept these query parameters:

Param Default Min Max Notes
page 1 1 1-based page number.
per_page 25 1 100 Clamped into the 1..100 range.

meta.total_pages is computed as ceil(total_results / per_page).

CSV export

Any list endpoint can be exported as CSV by adding the query parameter:

?format=csv

When format=csv is set, the endpoint bypasses pagination and returns the full result set (capped at 5000 rows) as a file download rather than JSON. The response uses:

The first row is the header (column names taken from the first record's keys). Array/object values are JSON-encoded into the cell; booleans become 1/0. Cell values are formula-injection-safe.

curl -s "https://seo.gtechgroup.it/api/seo/projects?format=csv" \
  -H "Authorization: Bearer YOUR_API_KEY" -o projects.csv

CORS / preflight

OPTIONS preflight requests are answered (HTTP 204) before authentication. Allowed methods: GET, POST, PATCH, DELETE, OPTIONS. Allowed headers: Authorization, Content-Type.

Request body

Write endpoints accept either form-encoded bodies (application/x-www-form-urlencoded) or a raw JSON body.


Standard error codes

HTTP When it occurs
400 Generic bad request (default error status).
401 Missing Authorization: Bearer header, or the token is invalid / the account lacks API access.
404 Unknown resource or sub-path; resource not owned by the caller; or the API is globally disabled.
405 Known resource but the HTTP method is not allowed for it (the Allow header lists permitted methods).
422 Validation error — a required field is missing or no updatable field was provided (e.g. name/domain, keyword, url, start_url, brand, competitors, prompt).
429 Rate limit exceeded (60/60s) or a per-endpoint hourly quota / refresh cooldown reached.

Additional statuses used by integration endpoints: 409 (Google/DataForSEO not configured or account not connected) and 502 (an upstream analysis or Google call failed).


GET /status

Returns the configuration/connection state for the calling account. No pagination.

Response fields

Field Type Meaning
dataforseo_configured boolean DataForSEO data source is configured (admin level).
google_configured boolean Google integration is configured (admin level).
google_connected boolean This account has a connected Google OAuth account.
google_email string | null The connected Google account email, or null if not connected.
api_enabled boolean The caller's plan has the API enabled.

Example

curl -s https://seo.gtechgroup.it/api/seo/status \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "data": {
    "dataforseo_configured": true,
    "google_configured": true,
    "google_connected": true,
    "google_email": "owner@example.com",
    "api_enabled": true
  }
}

GET / — Discovery index

GET /api/seo (the resource root, via GET) returns a discovery document describing the API.

Response fields

Field Type Value
name string "SEO API".
version string "v1".
base_url string The API base URL, <SITE_URL>/api/seo.
auth string "Authorization: Bearer <api_key>".
docs string URL of the API docs, <SITE_URL>/gtg-seo/api-docs.
resources array List of available resource names (see below).
notes string Free-text usage notes (pagination, CSV export, rate limit, CORS).

resources is:

status, projects, keywords, spider, geo, ai-visibility, ai-engine,
ai-competitor, prompt-research, keyword-research, search-console, analytics, alerts

Example

curl -s https://seo.gtechgroup.it/api/seo \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "data": {
    "name": "SEO API",
    "version": "v1",
    "base_url": "https://seo.gtechgroup.it/api/seo",
    "auth": "Authorization: Bearer <api_key>",
    "docs": "https://seo.gtechgroup.it/gtg-seo/api-docs",
    "resources": [
      "status", "projects", "keywords", "spider", "geo",
      "ai-visibility", "ai-engine", "ai-competitor", "prompt-research",
      "keyword-research", "search-console", "analytics", "alerts"
    ],
    "notes": "Liste paginate (?page=&per_page=, max 100); export CSV con ?format=csv; rate-limit 60/min; CORS abilitato."
  }
}

Rank Tracker (Projects, Keywords, Keyword Research)

REST endpoints for managing rank-tracking projects, their tracked keywords, and ad-hoc keyword research.

Common response envelope

Success responses use a JSON:API-style envelope:

{ "data": { ... }, "meta": { ... } }

meta is only present on paginated list responses. Error responses use:

{ "errors": [ { "title": "message", "status": "404" } ] }

Common errors

Status When
401 Missing/invalid Bearer key, inactive user, or API access not enabled for the plan.
404 Unknown resource/sub-path, or a project/keyword that does not exist or is not owned by the authenticated user. Title: not_found message.
405 Known resource but unsupported HTTP method (an Allow header lists the permitted verbs).
422 Validation error (missing required fields, nothing to update, empty keyword).
429 Rate limit reached (global API limit, refresh cooldown, or keyword-research hourly cap).
502 Upstream DataForSEO error during a refresh or research call.
409 DataForSEO data source not configured.

All collections are scoped to the authenticated user (user_id). Operations on a project/keyword owned by another user return 404.


GET /projects — list projects

Returns the authenticated user's rank-tracker projects, newest first (ordered by project_id DESC).

Path: GET /api/seo/projects

Query parameters

Param Type Default Notes
page int 1 Page number (min 1).
per_page int 25 Items per page, clamped to 1–100.
format string If csv, returns the full set (capped at 5000 rows) as a CSV file download instead of a JSON page.

Response data — array of project objects:

Field Type Description
id int Project ID.
name string Project display name.
domain string Normalized domain being tracked.
location_code int DataForSEO location code (geo target).
language_code string Language code (e.g. it, en).
keywords int Count of keywords tracked in the project.
gsc_property string | null Linked Google Search Console property.
ga4_property string | null Linked GA4 property ID (digits only).
last_datetime string | null Last refresh timestamp (Y-m-d H:i:s) or null.
datetime string Project creation timestamp.

Response meta: page, per_page, total_results, total_pages.

Example

curl -s "https://seo.gtechgroup.it/api/seo/projects?page=1&per_page=25" \
  -H "Authorization: Bearer <api_key>"
{
  "data": [
    {
      "id": 12,
      "name": "G Tech Group",
      "domain": "gtechgroup.it",
      "location_code": 2380,
      "language_code": "it",
      "keywords": 34,
      "gsc_property": "sc-domain:gtechgroup.it",
      "ga4_property": "123456789",
      "last_datetime": "2026-06-12 03:15:02",
      "datetime": "2026-05-01 10:22:41"
    }
  ],
  "meta": { "page": 1, "per_page": 25, "total_results": 1, "total_pages": 1 }
}

GET /projects/{id} — get one project (with stats + keywords)

Returns a single project including its keywords and a 90-day stats summary computed locally.

Path: GET /api/seo/projects/{id}

Path params: id (int) — project ID.

Response data

Field Type Description
id int Project ID.
name string Project name.
domain string Tracked domain.
location_code int DataForSEO location code.
language_code string Language code.
gsc_property string | null Linked GSC property.
ga4_property string | null Linked GA4 property ID.
last_datetime string | null Last refresh timestamp.
datetime string Creation timestamp.
stats object | null 90-day stats summary (see below); null if the stats helper is unavailable.
keywords array Tracked keywords (see below), ordered by keyword_id ASC.

keywords[] items

Field Type Description
id int Keyword ID.
keyword string The keyword phrase.
last_position int | null Most recent ranking position (null if never ranked/checked).
last_url string | null URL that ranked at the last check.
search_volume int | null Monthly search volume (null if unknown).
last_datetime string | null Timestamp of the last position check.

stats object (computed over rankings of the last 90 days)

Field Type Description
tracked int Number of keywords in the project.
ranking int Number of keywords with a known latest position.
avg_position float | null Average latest position across ranking keywords (1 decimal), or null.
buckets object Position distribution: top3, top10, top20, top50, top100, none (int counts).
gained array Up to 5 biggest improvers; each { keyword, delta, from, to } (delta/from null for new entries).
lost array Up to 5 biggest decliners; each { keyword, delta, from, to } (to null for dropped-out keywords).
timeline object Map of YYYY-MM-DD → average position that day (1 decimal).

Example

curl -s "https://seo.gtechgroup.it/api/seo/projects/12" \
  -H "Authorization: Bearer <api_key>"
{
  "data": {
    "id": 12,
    "name": "G Tech Group",
    "domain": "gtechgroup.it",
    "location_code": 2380,
    "language_code": "it",
    "gsc_property": "sc-domain:gtechgroup.it",
    "ga4_property": "123456789",
    "last_datetime": "2026-06-12 03:15:02",
    "datetime": "2026-05-01 10:22:41",
    "stats": {
      "tracked": 2,
      "ranking": 2,
      "avg_position": 7.5,
      "buckets": { "top3": 1, "top10": 1, "top20": 0, "top50": 0, "top100": 0, "none": 0 },
      "gained": [ { "keyword": "agenzia seo trento", "delta": 4, "from": 9, "to": 5 } ],
      "lost": [],
      "timeline": { "2026-06-10": 8.0, "2026-06-12": 7.5 }
    },
    "keywords": [
      {
        "id": 101,
        "keyword": "agenzia seo trento",
        "last_position": 5,
        "last_url": "https://gtechgroup.it/seo",
        "search_volume": 320,
        "last_datetime": "2026-06-12 03:15:02"
      }
    ]
  }
}

Errors: 404 if the project does not exist or is not owned by the caller.


POST /projects — create a project

Creates a new rank-tracker project for the authenticated user.

Path: POST /api/seo/projects

Request body

Field Type Required Notes
name string yes Trimmed, max 255 chars. Must be non-empty.
domain string yes Normalized (via gtg_seo_normalize_domain). Must be non-empty.
location_code int no Defaults to the configured default_location_code.
language_code string no Lowercased, stripped to [a-z-], max 8 chars. Defaults to the configured default_language_code, falling back to en.

Response: 201 Created with data: { "id": <new_project_id> }.

Example

curl -s -X POST "https://seo.gtechgroup.it/api/seo/projects" \
  -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{ "name": "G Tech Group", "domain": "gtechgroup.it", "location_code": 2380, "language_code": "it" }'
{ "data": { "id": 13 } }

Errors: 422 if name or domain is empty (I campi name e domain sono obbligatori.).


PATCH /projects/{id} — update a project

Partially updates a project. Only the fields present in the body are changed.

Path: PATCH /api/seo/projects/{id}

Path params: id (int).

Request body (all optional; at least one must be present)

Field Type Notes
name string Trimmed, max 255 chars.
domain string Normalized via gtg_seo_normalize_domain.
location_code int Cast to int.
language_code string Lowercased, stripped to [a-z-], max 8 chars.
gsc_property string | null Max 255 chars. Send null or "" to clear it.
ga4_property string | null Digits only. Send null or "" to clear it.

Response: data: { "id": <project_id> }.

Example

curl -s -X PATCH "https://seo.gtechgroup.it/api/seo/projects/12" \
  -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{ "name": "G Tech Group SRLS", "gsc_property": "sc-domain:gtechgroup.it" }'
{ "data": { "id": 12 } }

Errors: 404 if not found/owned; 422 if no recognized field is supplied (Nessun campo da aggiornare.).


POST /projects/{id}/keywords — add keywords

Adds keywords to a project, de-duplicating case-insensitively against existing keywords and within the submitted batch.

Path: POST /api/seo/projects/{id}/keywords

Path params: id (int).

Request body — provide one of:

Field Type Notes
keywords array of strings Preferred. Each entry trimmed, max 255 chars; empty entries skipped.
keyword string Alternative. A newline-separated list (\r\n, \r, or \n) split into individual keywords.

If keywords is a non-empty array it is used; otherwise keyword is split. At least one must yield a keyword.

Response: 201 Created with counts.

Field Type Description
added int Number of new keywords inserted.
skipped int Number skipped as duplicates (existing or repeated in batch).

Example (array form)

curl -s -X POST "https://seo.gtechgroup.it/api/seo/projects/12/keywords" \
  -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{ "keywords": ["agenzia seo trento", "consulente seo", "agenzia seo trento"] }'
{ "data": { "added": 2, "skipped": 1 } }

Example (newline string form)

curl -s -X POST "https://seo.gtechgroup.it/api/seo/projects/12/keywords" \
  -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{ "keyword": "agenzia seo trento\nconsulente seo" }'

Errors: 404 if project not found/owned; 422 if neither field yields a keyword (Fornire keyword (stringa) o keywords (array).).


POST /projects/{id}/refresh — refresh rankings

Triggers a synchronous refresh of all the project's keyword rankings via DataForSEO. Uses an internal claim + cooldown (~10 min) to avoid concurrent/duplicate refreshes.

Path: POST /api/seo/projects/{id}/refresh

Path params: id (int).

Request body: none.

Response: data: { "refreshed": <count> } — number of keywords successfully refreshed.

Example

curl -s -X POST "https://seo.gtechgroup.it/api/seo/projects/12/refresh" \
  -H "Authorization: Bearer <api_key>"
{ "data": { "refreshed": 34 } }

Errors:

Status Cause
409 DataForSEO data source not configured (not_configured).
404 Project not found/owned (not_found).
429 A refresh ran recently; in cooldown (~10 min).
502 Upstream API failed mid-run; message includes how many succeeded before the failure.

GET /projects/{id} keyword sub-resources

Project keywords are returned inline by GET /projects/{id}. Individual keyword operations live under the top-level /keywords resource (the keyword's own ID, not the project ID).

GET /keywords/{id}/history — keyword ranking history

Returns the position history for one keyword over a window of days.

Path: GET /api/seo/keywords/{id}/history

Path params: id (int) — keyword ID (must belong to the caller).

Query parameters

Param Type Default Notes
days int 90 Look-back window, clamped to 1–365.

Response data

Field Type Description
keyword string The keyword phrase.
history array Points ordered by datetime ascending.

Each history[] item: { "position": int|null, "url": string|null, "datetime": "Y-m-d H:i:s" }.

Example

curl -s "https://seo.gtechgroup.it/api/seo/keywords/101/history?days=30" \
  -H "Authorization: Bearer <api_key>"
{
  "data": {
    "keyword": "agenzia seo trento",
    "history": [
      { "position": 9, "url": "https://gtechgroup.it/seo", "datetime": "2026-06-10 03:10:00" },
      { "position": 5, "url": "https://gtechgroup.it/seo", "datetime": "2026-06-12 03:15:02" }
    ]
  }
}

DELETE /keywords/{id} — delete a keyword

Removes a single tracked keyword (and its rankings cascade).

Path: DELETE /api/seo/keywords/{id}

Path params: id (int) — keyword ID owned by the caller.

Response: data: { "id": <keyword_id> }.

curl -s -X DELETE "https://seo.gtechgroup.it/api/seo/keywords/101" \
  -H "Authorization: Bearer <api_key>"
{ "data": { "id": 101 } }

Errors: 404 if the keyword does not exist or is not owned by the caller.

Note: the /keywords resource only supports GET (history) and DELETE. There is no separate "list keywords" endpoint — keywords are listed inside GET /projects/{id}.

DELETE /projects/{id} — delete a project

Deletes the project; keywords and rankings cascade.

Path: DELETE /api/seo/projects/{id}

Response: data: { "id": <project_id> }.

curl -s -X DELETE "https://seo.gtechgroup.it/api/seo/projects/12" \
  -H "Authorization: Bearer <api_key>"

Errors: 404 if not found/owned.


POST /keyword-research — keyword research

Runs an on-demand keyword research lookup against DataForSEO Labs: a seed-keyword overview plus related keywords and questions. Results are server-side cached per keyword + location_code + language_code.

Path: POST /api/seo/keyword-research

Request body

Field Type Required Notes
keyword string yes Trimmed; must be non-empty. Lowercased internally.
location_code int no DataForSEO location code. Falls back to the configured default_location_code.
language_code string no Lowercased, stripped to [a-z-], max 8 chars. Falls back to the configured default_language_code.

The ideas limit is fixed server-side (100); it is not a request parameter.

Rate limit: max 60 research calls per user per rolling hour (cache-based counter). Exceeding it returns 429.

Response: 201 Created.

Field Type Description
seed object Metrics for the seed keyword (a "keyword metric" object, see below).
related array Related (non-question) keyword ideas, sorted by opportunity descending.
questions array Question-style keyword ideas, sorted by search_volume descending.
location_code int Resolved location code used.
language_code string Resolved language code used.
generated_at string Y-m-d H:i:s generation timestamp.

Keyword metric object (used for seed and each item in related/questions)

Field Type Description
keyword string The keyword phrase.
search_volume int | null Monthly search volume.
cpc float | null Cost-per-click.
competition float | null Competition index (0–1).
competition_level string | null e.g. LOW, MEDIUM, HIGH.
difficulty int | null Keyword difficulty score.
intent string | null Main search intent (e.g. informational, commercial).
monthly_searches array Per-month search-volume history from DataForSEO (each { year, month, search_volume }).
serp_features array SERP feature types present (e.g. featured_snippet, people_also_ask, video).
opportunity number | null Computed opportunity score (volume vs. difficulty).

Example

curl -s -X POST "https://seo.gtechgroup.it/api/seo/keyword-research" \
  -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{ "keyword": "agenzia seo", "location_code": 2380, "language_code": "it" }'
{
  "data": {
    "seed": {
      "keyword": "agenzia seo",
      "search_volume": 2400,
      "cpc": 6.12,
      "competition": 0.71,
      "competition_level": "HIGH",
      "difficulty": 58,
      "intent": "commercial",
      "monthly_searches": [ { "year": 2026, "month": 5, "search_volume": 2400 } ],
      "serp_features": [ "people_also_ask", "local_pack" ],
      "opportunity": 41.3
    },
    "related": [
      {
        "keyword": "agenzia seo trento",
        "search_volume": 320,
        "cpc": 4.05,
        "competition": 0.44,
        "competition_level": "MEDIUM",
        "difficulty": 27,
        "intent": "commercial",
        "monthly_searches": [],
        "serp_features": [ "local_pack" ],
        "opportunity": 63.0
      }
    ],
    "questions": [
      {
        "keyword": "cosa fa un'agenzia seo",
        "search_volume": 90,
        "cpc": null,
        "competition": null,
        "competition_level": null,
        "difficulty": 12,
        "intent": "informational",
        "monthly_searches": [],
        "serp_features": [ "people_also_ask" ],
        "opportunity": 70.0
      }
    ],
    "location_code": 2380,
    "language_code": "it",
    "generated_at": "2026-06-13 11:04:22"
  }
}

Errors:

Status Cause
422 keyword empty (Il campo keyword è obbligatorio.).
409 DataForSEO not configured (Fonte dati DataForSEO non configurata.).
429 Hourly research limit reached (Limite orario raggiunto.).
502 Upstream research failed (service error message or Ricerca non riuscita.).
405 Any method other than POST on /keyword-research.

SEO Spider

Endpoints for crawling a website and retrieving the resulting SEO audit (scans, crawled pages and issue breakdowns).

Base URL: https://seo.gtechgroup.it/api/seo Authentication: all requests require a Bearer token.

Authorization: Bearer <api_key>

Conventions


GET /spider

List the SEO Spider scans belonging to the authenticated user, newest first (ordered by scan_id DESC).

Query parameters

Name Type Default Description
page int 1 Page number.
per_page int 25 Results per page (max 100).
format string Set to csv to download the list as CSV (up to 5000 rows).

Per-scan fields (summary shape)

Field Type Description
id int Scan id.
name string Scan name (may be empty).
host string Crawled host.
status string crawling, completed, or failed.
score int | null Overall SEO score (0–100), null until computed.
pages_found int URLs discovered/enqueued.
pages_processed int Pages actually crawled.
errors_count int Number of error-level issues.
warnings_count int Number of warning-level issues.
started_datetime string | null When the crawl started.
finished_datetime string | null When the crawl finished.
datetime string Record creation timestamp.

curl

curl -s "https://seo.gtechgroup.it/api/seo/spider?page=1&per_page=25" \
  -H "Authorization: Bearer <api_key>"

Example response

{
  "data": [
    {
      "id": 42,
      "name": "Crawl gtechgroup.it",
      "host": "gtechgroup.it",
      "status": "completed",
      "score": 78,
      "pages_found": 312,
      "pages_processed": 312,
      "errors_count": 9,
      "warnings_count": 41,
      "started_datetime": "2026-06-13 09:14:02",
      "finished_datetime": "2026-06-13 09:31:55",
      "datetime": "2026-06-13 09:14:01"
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 25,
    "total_results": 1,
    "total_pages": 1
  }
}

GET /spider/{id}

Retrieve a single scan (detail shape). Returns the same fields as the list shape, plus — only while status is crawling — a pending field with the number of URLs still queued.

Path parameters

Name Type Description
id int Scan id. Must belong to the authenticated user.

Additional field (detail, crawling only)

Field Type Description
pending int URLs still in the queue (present only when status = crawling).

curl

curl -s "https://seo.gtechgroup.it/api/seo/spider/42" \
  -H "Authorization: Bearer <api_key>"

Example response (in progress)

{
  "data": {
    "id": 42,
    "name": "Crawl gtechgroup.it",
    "host": "gtechgroup.it",
    "status": "crawling",
    "score": null,
    "pages_found": 120,
    "pages_processed": 75,
    "errors_count": 2,
    "warnings_count": 18,
    "started_datetime": "2026-06-13 09:14:02",
    "finished_datetime": null,
    "datetime": "2026-06-13 09:14:01",
    "pending": 45
  }
}

Errors

Status Condition
404 Scan not found or not owned by the authenticated user.

GET /spider/{id}/issues

Aggregated issue breakdown across all crawled pages of a scan. Counts each issue code's occurrences and rolls them up by severity.

Path parameters

Name Type Description
id int Scan id. Must belong to the authenticated user.

Response fields

Field Type Description
by_severity object Totals per severity: { "major": int, "moderate": int, "minor": int }. Counts total occurrences across pages.
issues array One entry per distinct issue code, sorted by pages descending.

Each issues[] entry:

Field Type Description
code string Issue code (e.g. title_missing, meta_desc_missing, images_no_alt).
severity string major, moderate, or minor (codes not in the known map default to minor).
pages int Number of pages affected by this issue code.

Issue codes by severity (from SpiderService::SEVERITY)

Note: the API response does not include a per-issue fix field — only code, severity, and pages.

curl

curl -s "https://seo.gtechgroup.it/api/seo/spider/42/issues" \
  -H "Authorization: Bearer <api_key>"

Example response

{
  "data": {
    "by_severity": {
      "major": 11,
      "moderate": 53,
      "minor": 128
    },
    "issues": [
      { "code": "images_no_alt",     "severity": "minor",    "pages": 64 },
      { "code": "meta_desc_missing", "severity": "moderate", "pages": 30 },
      { "code": "title_missing",     "severity": "major",    "pages": 6 }
    ]
  }
}

Errors

Status Condition
404 Scan not found or not owned by the authenticated user.

GET /spider/{id}/pages

List the crawled pages of a scan, paginated, ordered by page_id ascending.

Path parameters

Name Type Description
id int Scan id. Must belong to the authenticated user.

Query parameters

Name Type Default Description
page int 1 Page number.
per_page int 25 Results per page (max 100).
status int Filter by exact HTTP status code (e.g. 404).
issues any (truthy) If present/non-empty, return only pages that have at least one issue.
format string Set to csv to download the list as CSV (up to 5000 rows).

Per-page fields

Field Type Description
id int Page id.
url string Page URL.
status_code int HTTP status code (0 if the fetch failed).
score int | null Page SEO score, null if not computed.
title string | null <title> text.
meta_description string | null Meta description.
h1 string | null First H1 text.
canonical string | null Canonical URL.
is_indexable bool Whether the page is indexable.
is_noindex bool Whether the page has a noindex directive.
words_count int Word count of the page body.
images_no_alt int Number of images missing alt.
internal_links int Internal links count.
external_links int External links count.
schema_types string | null Detected schema.org types (raw stored value, e.g. comma-separated).
issues array Issue codes for this page (decoded from stored JSON; [] if none).

Note the JSON key names differ from the underlying columns: internal_links_count is returned as internal_links and external_links_count as external_links.

curl

# All pages
curl -s "https://seo.gtechgroup.it/api/seo/spider/42/pages?page=1&per_page=50" \
  -H "Authorization: Bearer <api_key>"

# Only pages with issues
curl -s "https://seo.gtechgroup.it/api/seo/spider/42/pages?issues=1" \
  -H "Authorization: Bearer <api_key>"

# Only 404 pages
curl -s "https://seo.gtechgroup.it/api/seo/spider/42/pages?status=404" \
  -H "Authorization: Bearer <api_key>"

# CSV export
curl -s "https://seo.gtechgroup.it/api/seo/spider/42/pages?format=csv" \
  -H "Authorization: Bearer <api_key>" -o spider_pages.csv

Example response

{
  "data": [
    {
      "id": 1501,
      "url": "https://gtechgroup.it/servizi/",
      "status_code": 200,
      "score": 82,
      "title": "Servizi - G Tech Group",
      "meta_description": "I nostri servizi di sviluppo e SEO.",
      "h1": "I nostri servizi",
      "canonical": "https://gtechgroup.it/servizi/",
      "is_indexable": true,
      "is_noindex": false,
      "words_count": 642,
      "images_no_alt": 2,
      "internal_links": 38,
      "external_links": 4,
      "schema_types": "Organization,WebPage",
      "issues": ["images_no_alt", "meta_desc_long"]
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 50,
    "total_results": 1,
    "total_pages": 1
  }
}

Errors

Status Condition
404 Scan not found or not owned by the authenticated user.

POST /spider

Start a new crawl. Returns immediately with the new scan id; crawling proceeds asynchronously (driven by cron + live polling), so poll GET /spider/{id} to track progress.

Request body (JSON)

Field Type Required Default Description
start_url string yes* URL to start crawling from. *Either start_url or url must be provided and non-empty.
url string yes* Alias accepted if start_url is absent.
name string no "" Friendly name for the scan.
max_pages int no plan limit (gtg_seo_spider_pages_limit, fallback 200) Max pages to crawl. Capped to the plan limit when set, then hard-capped server-side to 110000.
max_depth int no 10 Max crawl depth. Clamped server-side to 050.
speed string no fast One of safe, fast, turbo (courtesy delay between pages: 4 / 2 / 1 s respectively). Any other value falls back to fast at the API layer.
include_subdomains bool no false Crawl subdomains of the host as well.
respect_robots bool no false Honor robots.txt rules.
use_sitemap bool no false Seed the queue from the site's sitemap.
exclude_paths string no "" Path-exclusion rules (newline/glob style). Truncated to 2000 chars at the API layer (2048 in storage).

Success response — HTTP 201:

Field Type Description
id int New scan id.
status string Always crawling on creation.

curl

curl -s -X POST "https://seo.gtechgroup.it/api/seo/spider" \
  -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Crawl gtechgroup.it",
    "start_url": "https://gtechgroup.it/",
    "max_pages": 500,
    "max_depth": 10,
    "speed": "fast",
    "include_subdomains": false,
    "respect_robots": true,
    "use_sitemap": true,
    "exclude_paths": "/wp-admin/\n/cart/"
  }'

Example response

{
  "data": {
    "id": 43,
    "status": "crawling"
  }
}

Errors

Status Condition
422 start_url (and url) missing/empty — "Il campo start_url è obbligatorio."
422 The scan could not be created/started (service error, or "Impossibile avviare la scansione.").

DELETE /spider/{id}

Delete a scan. Cascades to its crawled pages, queue, and link records.

Path parameters

Name Type Description
id int Scan id. Must belong to the authenticated user.

Success response

Field Type Description
id int The deleted scan id.

curl

curl -s -X DELETE "https://seo.gtechgroup.it/api/seo/spider/43" \
  -H "Authorization: Bearer <api_key>"

Example response

{
  "data": {
    "id": 43
  }
}

Errors

Status Condition
404 Scan not found or not owned by the authenticated user.

REST endpoints for the Generative Engine Optimization (GEO) and AI-search modules of the GTG-SEO suite. These five resources share a common implementation (resource_config() + generic_resource() + run_action()), so their request/response shapes follow the same pattern; only the table, fields and POST runner differ.

Resources at a glance

Resource DB table id column
geo gtg_seo_geo_audits audit_id
ai-visibility gtg_seo_ai_visibility run_id
ai-engine gtg_seo_ai_engine_tests test_id
ai-competitor gtg_seo_ai_competitor report_id
prompt-research gtg_seo_prompt_research prompt_id

Common behaviour (all five resources)

These rules are enforced by the shared generic_resource() / cast_row() logic and apply identically to every resource below.

Scoping. All rows are scoped to the authenticated user (WHERE user_id = <uid>). The user_id column is stripped from single-item responses.

ID normalization (cast_row). The native id column (audit_id, run_id, test_id, report_id, prompt_id) is renamed to id and cast to integer in every response. The following columns, when present, are cast to integer: geo_score, score, prompts_total, cited_count, first_count, pages_count. has_llms_txt is cast to boolean. avg_position is cast to float.

GET /{resource} (list). Returns the list columns defined per resource (see each section), ordered by id column DESC, paginated.

GET /{resource}/{id} (single item). Returns the full DB row (all columns), with id normalization applied and user_id removed. Any of these JSON payload columns, when present and stored as a string, are decoded into objects before being returned: result, checks, recommendations, ai_crawlers, competitors. Returns 404 if no matching row for the user.

POST /{resource} (create / run). There is no plain insert — a POST to the collection triggers the resource's analysis runner (run_action), which is the costly operation (OpenAI / DataForSEO). On success it returns the freshly produced result with HTTP 201. Each runner is rate-limited to 30 runs/hour per user (429 when exceeded). Validation failures return 422; analysis failures return 502.

DELETE /{resource}/{id}. Not supported for these five resources — generic_resource() only handles GET (list / single) and POST (run). A DELETE will not match and is not routed here.


GEO — Generative Engine Optimization audit

geo audits a URL for how well it is optimized for generative/AI engines (LLM-friendliness): page count, presence of an llms.txt, and a computed GEO score.

Table: gtg_seo_geo_audits · id: audit_id

GET /geo — list

List columns: audit_id (→ id), host, url, geo_score, has_llms_txt, pages_count, datetime.

curl -H "Authorization: Bearer <api_key>" \
  "https://seo.gtechgroup.it/api/seo/geo?page=1&per_page=25"
{
  "data": [
    {
      "host": "example.com",
      "url": "https://example.com/",
      "geo_score": 72,
      "has_llms_txt": true,
      "pages_count": 18,
      "datetime": "2026-06-13 10:42:11",
      "id": 14
    }
  ]
}

GET /geo/{id} — single audit

Returns the full row; checks, recommendations and ai_crawlers JSON columns (when present) are decoded.

curl -H "Authorization: Bearer <api_key>" \
  "https://seo.gtechgroup.it/api/seo/geo/14"

POST /geo — run an audit

Body params read by the runner:

Field Required Notes
url yes* The URL/domain to audit
domain Used as a fallback if url is absent

* At least one of url / domain must be a non-empty string, otherwise 422 (Il campo url è obbligatorio.).

Runs GeoAuditService::audit($url, $uid). Returns 201 with the audit result, or 502 (Audit non riuscito.) if the service returns null.

curl -X POST -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/"}' \
  "https://seo.gtechgroup.it/api/seo/geo"

AI Visibility — brand presence across AI engines

ai-visibility measures how often and how prominently a brand is cited by AI engines across a set of prompts: citation counts, first-mention counts, average position and an overall score, per engine.

Table: gtg_seo_ai_visibility · id: run_id

GET /ai-visibility — list

List columns: run_id (→ id), brand, host, engine, score, prompts_total, cited_count, first_count, avg_position, datetime.

curl -H "Authorization: Bearer <api_key>" \
  "https://seo.gtechgroup.it/api/seo/ai-visibility?per_page=50"
{
  "data": [
    {
      "brand": "Acme",
      "host": "acme.com",
      "engine": "openai",
      "score": 64,
      "prompts_total": 10,
      "cited_count": 6,
      "first_count": 2,
      "avg_position": 1.83,
      "datetime": "2026-06-13 09:15:00",
      "id": 7
    }
  ]
}

GET /ai-visibility/{id} — single run

Returns the full row; result JSON column (when present) is decoded.

POST /ai-visibility — run an analysis

Body params read by the runner:

Field Required Notes
brand yes Non-empty string, else 422 (Il campo brand è obbligatorio.)
domain no Brand domain (passed to the service)
prompts no Array of prompts; ignored unless it is a non-empty array
topic no Topic string

Runs AiVisibilityService::analyze($brand, $domain, $prompts, $uid, $topic). Returns 201 with the result, or 502 with the service error message ($svc->error or Analisi non riuscita.).

curl -X POST -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{
        "brand": "Acme",
        "domain": "acme.com",
        "topic": "industrial pumps",
        "prompts": ["best industrial pump brands", "who makes reliable pumps"]
      }' \
  "https://seo.gtechgroup.it/api/seo/ai-visibility"

AI Engine — keyword/URL AI-readiness test

ai-engine tests how a specific URL performs for a given keyword from an AI-engine perspective, producing a score.

Table: gtg_seo_ai_engine_tests · id: test_id

GET /ai-engine — list

List columns: test_id (→ id), keyword, url, score, datetime.

curl -H "Authorization: Bearer <api_key>" \
  "https://seo.gtechgroup.it/api/seo/ai-engine"
{
  "data": [
    {
      "keyword": "industrial pumps",
      "url": "https://acme.com/pumps",
      "score": 58,
      "datetime": "2026-06-13 11:02:44",
      "id": 3
    }
  ]
}

GET /ai-engine/{id} — single test

Returns the full row; result JSON column (when present) is decoded.

POST /ai-engine — run a test

Body params read by the runner:

Field Required Notes
keyword yes Both keyword and url must be non-empty, else 422 (I campi keyword e url sono obbligatori.)
url yes The URL to test

Runs AiEngineService::analyze($keyword, $url, $uid). Returns 201 with the result, or 502 with the service error message.

curl -X POST -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{"keyword":"industrial pumps","url":"https://acme.com/pumps"}' \
  "https://seo.gtechgroup.it/api/seo/ai-engine"

AI Competitor — brand vs competitors AI report

ai-competitor generates a comparative report of a brand against one or more competitors as seen by AI engines.

Table: gtg_seo_ai_competitor · id: report_id

GET /ai-competitor — list

List columns: report_id (→ id), brand, competitors, datetime.

curl -H "Authorization: Bearer <api_key>" \
  "https://seo.gtechgroup.it/api/seo/ai-competitor"
{
  "data": [
    {
      "brand": "Acme",
      "competitors": "Globex, Initech",
      "datetime": "2026-06-13 08:30:00",
      "id": 2
    }
  ]
}

GET /ai-competitor/{id} — single report

Returns the full row; competitors and result JSON columns (when present) are decoded.

POST /ai-competitor — run a report

Body params read by the runner:

Field Required Notes
brand yes Both brand and competitors must be non-empty, else 422 (I campi brand e competitors sono obbligatori.)
competitors yes Competitor list (string)

Runs AiCompetitorService::analyze($brand, $competitors, $uid). Returns 201 with the result, or 502 with the service error message.

curl -X POST -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{"brand":"Acme","competitors":"Globex, Initech"}' \
  "https://seo.gtechgroup.it/api/seo/ai-competitor"

AI Prompt Research — prompt expansion / research

prompt-research expands a seed prompt into research output (e.g. related AI-search prompts/variations) for a given language.

Table: gtg_seo_prompt_research · id: prompt_id

Note: Prompt Research uses its own cache-based hourly limiter (pr_rate_<uid>, 30/hour) instead of the shared table rate guard, and analyze() is called without a user id.

GET /prompt-research — list

List columns: prompt_id (→ id), prompt, language, datetime.

curl -H "Authorization: Bearer <api_key>" \
  "https://seo.gtechgroup.it/api/seo/prompt-research"
{
  "data": [
    {
      "prompt": "best CRM for small business",
      "language": "en",
      "datetime": "2026-06-13 12:00:00",
      "id": 9
    }
  ]
}

GET /prompt-research/{id} — single item

Returns the full row; result JSON column (when present) is decoded.

POST /prompt-research — run research

Body params read by the runner:

Field Required Notes
prompt yes Non-empty string, else 422 (Il campo prompt è obbligatorio.)

Runs PromptResearchService::analyze($prompt). Returns 201 with the result, or 502 with the service error message. Rate limited to 30 runs/hour per user (429 when exceeded).

curl -X POST -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"best CRM for small business"}' \
  "https://seo.gtechgroup.it/api/seo/prompt-research"

Search Console, Analytics & Alerts

Base URL: https://seo.gtechgroup.it/api/seo Auth (all endpoints): Authorization: Bearer <api_key>

Endpoint Requires Google account connected?
/search-console Yes
/analytics Yes
/alerts No (reads local DB)

Google connection requirement (search-console & analytics)

Both /search-console and /analytics call google_client() before doing anything else. This returns errors (and stops the request) in three cases:

If a downstream Google API call fails after connection, the endpoint also returns 502 with the Google client error message.

Shared date window (range)

Both Google endpoints use the same window logic:


GET /search-console

Live Google Search Console performance for one verified site/property.

Sub-resource: list sites

GET /api/seo/search-console/sites

Returns a flat JSON array of the site URLs available to the connected Google account (the siteUrl values, e.g. sc-domain:example.com or https://example.com/). The site list is cached for 6 hours per user.

Main report

GET /api/seo/search-console
Query parameters
Param Required Description
site Conditional The GSC siteUrl to query. Must be one of the values from /search-console/sites. Optional only if the account has exactly one site, in which case it is auto-selected.
range No Window in days: 7, 28, or 90. Default 28. See window logic above.
422 case

If site is missing (and the account does not have exactly one site to auto-select) or is not a valid site for the account:

Response (data object)
Field Type Description
site string The resolved siteUrl.
range_days int Window size (7 / 28 / 90).
start string Start date YYYY-MM-DD (inclusive).
end string End date YYYY-MM-DD (inclusive).
totals object Aggregate metrics for the window (see below).
queries array Top 25 search queries (row shape below).
pages array Top 25 pages (row shape below).

totals object:

Field Type Description
clicks int Total clicks.
impressions int Total impressions.
ctr float Click-through rate as a percentage, rounded to 2 decimals (raw GSC ratio × 100).
position float Average position, rounded to 1 decimal.

Each row in queries and pages (GSC row shape):

Field Type Description
key string The query text (for queries) or page URL (for pages).
clicks int Clicks for that key.
impressions int Impressions for that key.
ctr float CTR as a percentage, rounded to 2 decimals.
position float Average position, rounded to 1 decimal.

The full report bundle is cached for 3 hours per (user, site, start, end).

curl
curl -s "https://seo.gtechgroup.it/api/seo/search-console?site=sc-domain:example.com&range=28" \
  -H "Authorization: Bearer <api_key>"
Example response
{
  "data": {
    "site": "sc-domain:example.com",
    "range_days": 28,
    "start": "2026-06-01",
    "end": "2026-06-28",
    "totals": {
      "clicks": 1234,
      "impressions": 56789,
      "ctr": 2.17,
      "position": 14.3
    },
    "queries": [
      { "key": "esempio keyword", "clicks": 210, "impressions": 4300, "ctr": 4.88, "position": 6.2 },
      { "key": "altra query",     "clicks": 95,  "impressions": 2100, "ctr": 4.52, "position": 9.8 }
    ],
    "pages": [
      { "key": "https://example.com/", "clicks": 540, "impressions": 12000, "ctr": 4.5, "position": 7.1 }
    ]
  }
}

GET /analytics

Live Google Analytics 4 (GA4) report for one property.

Sub-resource: list properties

GET /api/seo/analytics/properties

Returns a JSON array of the raw GA4 property objects available to the connected account (each contains a property field like properties/123456789). Cached for 6 hours per user.

Main report

GET /api/seo/analytics
Query parameters
Param Required Description
property Conditional GA4 property id. Non-digit characters are stripped, so both properties/123456789 and 123456789 are accepted; the numeric id must match one of the account's properties. Optional only if the account has exactly one property, in which case it is auto-selected.
range No Window in days: 7, 28, or 90. Default 28. See window logic above.
422 case

If property is missing (and there isn't exactly one property to auto-select) or does not match a property of the account:

Response (data object)
Field Type Description
property string The resolved numeric property id.
range_days int Window size (7 / 28 / 90).
start string Start date YYYY-MM-DD (inclusive).
end string End date YYYY-MM-DD (inclusive).
totals object Aggregate GA4 metrics (see below).
top_pages array Top 15 pages by page views (see below).

totals object:

Field Type Description
sessions int Sessions (sessions).
active_users int Active users (activeUsers).
new_users int New users (newUsers).
page_views int Page views (screenPageViews).
engagement_rate float Engagement rate as a percentage, rounded to 1 decimal (engagementRate × 100).
avg_session_duration float Average session duration in seconds, rounded to 1 decimal (averageSessionDuration).

Each row in top_pages:

Field Type Description
page string The page path (pagePath dimension).
views int Page views for that path.

The bundle is cached for 3 hours per (user, property, start, end).

curl
curl -s "https://seo.gtechgroup.it/api/seo/analytics?property=123456789&range=7" \
  -H "Authorization: Bearer <api_key>"
Example response
{
  "data": {
    "property": "123456789",
    "range_days": 7,
    "start": "2026-06-22",
    "end": "2026-06-28",
    "totals": {
      "sessions": 4821,
      "active_users": 3990,
      "new_users": 2700,
      "page_views": 11230,
      "engagement_rate": 62.4,
      "avg_session_duration": 95.7
    },
    "top_pages": [
      { "page": "/", "views": 3120 },
      { "page": "/blog/articolo", "views": 880 }
    ]
  }
}

/alerts

Local SEO alerts for the authenticated user. Does not require a Google connection — reads from the gtg_seo_alerts table.

GET /alerts — list alerts

GET /api/seo/alerts
Query parameters
Param Required Description
is_read No Filter by read state. Cast to boolean → 0 (unread) or 1 (read). Omit to return all.
page No Page number, min 1. Default 1.
per_page No Items per page, clamped to 1100. Default 25.
format No csv to export the full set (capped at 5000 rows) as a CSV download instead of paginated JSON.

Results are ordered by datetime descending.

Response — one object per alert
Field Type Description
id int Alert id (alert_id).
type string Alert type.
severity string Alert severity.
message string Human-readable message.
is_read bool Whether the alert has been marked read.
datetime string Timestamp the alert was created.
Pagination (meta)

List responses include a meta block:

"meta": {
  "page": 1,
  "per_page": 25,
  "total_results": 137,
  "total_pages": 6
}
curl
curl -s "https://seo.gtechgroup.it/api/seo/alerts?is_read=0&page=1&per_page=25" \
  -H "Authorization: Bearer <api_key>"

CSV export:

curl -s "https://seo.gtechgroup.it/api/seo/alerts?format=csv" \
  -H "Authorization: Bearer <api_key>" -o alerts.csv
Example response
{
  "data": [
    {
      "id": 482,
      "type": "ranking_drop",
      "severity": "high",
      "message": "La keyword \"esempio\" e scesa di 12 posizioni.",
      "is_read": false,
      "datetime": "2026-06-29 08:14:02"
    },
    {
      "id": 481,
      "type": "new_backlink",
      "severity": "info",
      "message": "Rilevato un nuovo backlink da example.org.",
      "is_read": true,
      "datetime": "2026-06-28 22:01:11"
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 25,
    "total_results": 137,
    "total_pages": 6
  }
}

PATCH /alerts — mark read / unread

Mark a single alert, or all of the user's alerts, as read or unread.

PATCH /api/seo/alerts/{id}    # one alert
PATCH /api/seo/alerts         # all alerts for the user
Request body (JSON)
Field Type Description
is_read bool Target read state. Cast to 0/1. Defaults to 1 (read) if omitted.
Responses
curl
# mark one alert read
curl -s -X PATCH "https://seo.gtechgroup.it/api/seo/alerts/482" \
  -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{"is_read": true}'

# mark all alerts read
curl -s -X PATCH "https://seo.gtechgroup.it/api/seo/alerts" \
  -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{"is_read": true}'

Documentazione generata da analisi diretta del codice sorgente (app/controllers/api/GtgSeoApi.php). © G Tech Group S.R.L.S.


Novità — endpoint aggiunti/estesi (aggiornamento 2026-07)

Questa release aggiunge nuovi endpoint e parametri all'API. Autenticazione e convenzioni invariate.

Account — rotazione API key

Lookup location & lingue

Keyword — nuove operazioni e campi

Progetti

SEO Spider

Search Console — filtri avanzati

GET /api/seo/search-console?site=… accetta ora (modalità custom):

Analytics (GA4) — metriche/dimensioni flessibili

GET /api/seo/analytics?property=… accetta ora (modalità custom):

Alert su canali

Gli alert generati (rank drop, spider 5xx, SSL in scadenza, blocco crawler AI, ecc.) vengono ora inviati anche ai canali configurati dall'utente (webhook, Slack, Discord, Telegram, …) tramite i Notification Handler del sistema, oltre all'email.