curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/seo/scan \
--header 'api_key: <api-key>'import requests
url = "https://app.base44.com/api/apps/{app_id}/seo/scan"
headers = {"api_key": "<api-key>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {api_key: '<api-key>'}};
fetch('https://app.base44.com/api/apps/{app_id}/seo/scan', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.base44.com/api/apps/{app_id}/seo/scan",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"api_key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/seo/scan"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("api_key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.base44.com/api/apps/{app_id}/seo/scan")
.header("api_key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/seo/scan")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["api_key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"result": {
"score": {
"overall": 82,
"grade": "B",
"categories": {
"meta_tags": {
"failures": 0,
"passed": 4,
"score": 80,
"warnings": 1
}
}
},
"scanned_at": "2026-09-03T09:15:00+00:00",
"checks": [
{
"category": "ai_discoverability",
"description": "Turn on the AI site guide so AI engines can understand your app.",
"id": "llms_txt",
"status": "warn",
"title": "AI-readable site guide is off"
}
]
}
}Run SEO scan
Scans the app for SEO problems and returns a score with the checks behind it.
The scan reads the app’s own configuration and also fetches the live site over HTTP to check what it really serves for robots.txt, sitemap.xml and the home page. Those three run in parallel with a five-second timeout each and one retry, so expect a few seconds, and up to about ten when the live site is slow to answer. An app that isn’t published yet still scans, but those live checks have nothing to fetch. What you get back for one is the gating failure itself: Base44 drops the failures that merely restate it, and collapses the unreachable-host warnings into a single row rather than repeating one per probe.
Base44 then stores the result, so Get SEO score and Get last SEO scan serve this scan until the next one runs. That makes scanning the write in this group: prefer the two report endpoints when you only need the last result. Both writes are best-effort and can’t fail the scan, so a 200 here is the scan rather than a promise about what the report endpoints will say. Read them back if you need to be sure.
The score and the checklist are stored separately, and two scans running at once can interleave those two writes. So if you read both report endpoints, compare their scanned_at before treating the numbers as one scan’s.
Each check that Base44 can act on carries a fix_action. Read it to see what the builder offers; applying one isn’t part of this API. score.overall and the per-category counts are renormalized during scoring, so they don’t add up to a plain count of failing checks. Use failures from Get SEO score for that.
This endpoint is limited to 30 requests per minute per app, and that budget is shared with the app’s other SEO endpoints.
curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/seo/scan \
--header 'api_key: <api-key>'import requests
url = "https://app.base44.com/api/apps/{app_id}/seo/scan"
headers = {"api_key": "<api-key>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {api_key: '<api-key>'}};
fetch('https://app.base44.com/api/apps/{app_id}/seo/scan', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.base44.com/api/apps/{app_id}/seo/scan",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"api_key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/seo/scan"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("api_key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.base44.com/api/apps/{app_id}/seo/scan")
.header("api_key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/seo/scan")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["api_key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"result": {
"score": {
"overall": 82,
"grade": "B",
"categories": {
"meta_tags": {
"failures": 0,
"passed": 4,
"score": 80,
"warnings": 1
}
}
},
"scanned_at": "2026-09-03T09:15:00+00:00",
"checks": [
{
"category": "ai_discoverability",
"description": "Turn on the AI site guide so AI engines can understand your app.",
"id": "llms_txt",
"status": "warn",
"title": "AI-readable site guide is off"
}
]
}
}Authorizations
Personal API key.
Path Parameters
ID of the app to scan.
Response
The scan Base44 just ran.
A scan Base44 just ran, wrapped in the field the endpoint returns it under.
The scan Base44 just ran. Stored on a best-effort basis, so the report endpoints usually serve this same scan until the next one.
Show child attributes
Show child attributes
Was this page helpful?