> ## Documentation Index
> Fetch the complete documentation index at: https://docs.base44.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Run SEO scan

> <Info>This API is in beta. Endpoints, fields, and behavior may still change, so avoid depending on it in production.</Info>

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](/api-reference/get-seo-score) and [Get last SEO scan](/api-reference/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](/api-reference/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.

<Warning>The response includes fields beyond the ones documented here. Don't rely on undocumented response fields, as they can change at any time.</Warning>



## OpenAPI

````yaml /developers/references/app-management/app-management-openapi.json post /api/apps/{app_id}/seo/scan
openapi: 3.1.0
info:
  title: Base44 App Management API
  version: 1.0.0
servers:
  - url: https://app.base44.com
security:
  - ApiKeyAuth: []
paths:
  /api/apps/{app_id}/seo/scan:
    post:
      summary: Run SEO scan
      description: >-
        <Info>This API is in beta. Endpoints, fields, and behavior may still
        change, so avoid depending on it in production.</Info>


        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](/api-reference/get-seo-score) and [Get last SEO
        scan](/api-reference/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](/api-reference/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.


        <Warning>The response includes fields beyond the ones documented here.
        Don't rely on undocumented response fields, as they can change at any
        time.</Warning>
      operationId: run_seo_scan_api_apps__app_id__seo_scan_post
      parameters:
        - name: app_id
          in: path
          required: true
          schema:
            type: string
            description: ID of the app to scan.
            title: App Id
          description: ID of the app to scan.
          example: 6820f3a4e7b91d003c45a1f2
      responses:
        '200':
          description: The scan Base44 just ran.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SEOScanReportResponse'
        '401':
          description: Missing or invalid credentials.
        '403':
          description: >-
            You don't have access to this app, or you used a workspace API key.
            These endpoints accept a user's credentials only.
        '404':
          description: App not found.
        '429':
          description: Rate limit exceeded (30 requests per minute).
components:
  schemas:
    SEOScanReportResponse:
      properties:
        result:
          $ref: '#/components/schemas/SEOScanReport'
          description: >-
            The scan Base44 just ran. Stored on a best-effort basis, so the
            report endpoints usually serve this same scan until the next one.
      type: object
      required:
        - result
      title: SEOScanReportResponse
      description: >-
        A scan Base44 just ran, wrapped in the field the endpoint returns it
        under.
    SEOScanReport:
      properties:
        score:
          $ref: '#/components/schemas/SEOOverallScore'
          description: The score this scan produced.
        checks:
          items:
            $ref: '#/components/schemas/SEOCheckResult'
          type: array
          title: Checks
          description: >-
            The checks behind the score, in the order Base44 reports them. Not
            every check it ran: a failure that only restates an upstream one is
            dropped in favour of the root cause, so an unpublished or
            login-gated app returns the one gating failure rather than the dozen
            findings that follow from it.
          example:
            - 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
        scanned_at:
          type: string
          title: Scanned At
          description: When the scan ran, as an ISO 8601 UTC timestamp.
          example: '2026-09-03T09:15:00+00:00'
      type: object
      required:
        - score
        - scanned_at
      title: SEOScanReport
      description: 'An SEO scan: its score and every check behind it.'
    SEOOverallScore:
      properties:
        overall:
          type: integer
          title: Overall
          description: Overall SEO score for the app, 0 to 100.
          example: 82
        grade:
          type: string
          title: Grade
          description: 'Letter grade for `overall`: `A`, `B`, `C`, `D` or `F`.'
          example: B
        categories:
          additionalProperties:
            $ref: '#/components/schemas/SEOCategoryScore'
          type: object
          title: Categories
          description: >-
            Per-category breakdown, keyed by the same category names the checks
            carry: `meta_tags`, `crawlability`, `structured_data`,
            `ai_discoverability` and `content_quality`. Base44 can add a
            category, so read the map rather than assuming these five. Every
            category in the map is scored, including one this scan ran no checks
            for, which scores 100 with all three counts at zero.
          example:
            meta_tags:
              failures: 0
              passed: 4
              score: 80
              warnings: 1
      type: object
      required:
        - overall
        - grade
      title: SEOOverallScore
      description: The scan's score, overall and per category.
    SEOCheckResult:
      properties:
        id:
          type: string
          title: Id
          description: >-
            Identifier of the check. Stable across scans, but a check can be
            missing from a later scan rather than change status, because Base44
            collapses cascading failures into their root cause.
          example: llms_txt
        category:
          type: string
          title: Category
          description: >-
            Which part of the score this check feeds: `meta_tags`,
            `crawlability`, `structured_data`, `ai_discoverability` or
            `content_quality`.
          example: ai_discoverability
        title:
          type: string
          title: Title
          description: >-
            Short name of the check, phrased for the finding rather than the
            subject, so it changes with `status`.
          example: AI-readable site guide is off
        status:
          type: string
          enum:
            - pass
            - warn
            - fail
          title: Status
          description: >-
            Outcome of the check. A `warn` scores 50 against the category and a
            `fail` scores 0. Not every check can return all three.
          example: warn
        description:
          type: string
          title: Description
          description: What the check found, written for the app's owner.
          example: Turn on the AI site guide so AI engines can understand your app.
        details:
          items: {}
          type: array
          title: Details
          description: >-
            Supporting detail for the finding, usually one object per page or
            entity involved. The keys differ per check, so treat the entries as
            opaque.
          example:
            - issue: Duplicate title
              page: Pricing
        fix_action:
          anyOf:
            - $ref: '#/components/schemas/SEOFixAction'
            - type: 'null'
          description: >-
            The remedy the Base44 builder offers, or `null` when the check
            passed or can't be fixed in-app. Read it for display: applying it
            isn't part of this API.
        info_only:
          type: boolean
          title: Info Only
          description: >-
            `true` when the finding is environmental, such as DNS or hosting,
            and can't be resolved from inside the app.
          default: false
          example: false
      type: object
      required:
        - id
        - category
        - title
        - status
        - description
      title: SEOCheckResult
      description: One check from the SEO scan.
    SEOCategoryScore:
      properties:
        score:
          type: integer
          title: Score
          description: >-
            Score for this category, 0 to 100. A passing check counts 100, a
            warning 50 and a failure 0, averaged over the category's checks.
          example: 80
        passed:
          type: integer
          title: Passed
          description: >-
            Checks in this category that passed. A plain count, so the three
            counts add up to the category's checks in `checks`.
          default: 0
          example: 4
        warnings:
          type: integer
          title: Warnings
          description: Checks in this category that returned a warning.
          default: 0
          example: 1
        failures:
          type: integer
          title: Failures
          description: Checks in this category that failed.
          default: 0
          example: 0
      type: object
      required:
        - score
      title: SEOCategoryScore
      description: One category's slice of the overall score.
    SEOFixAction:
      properties:
        type:
          type: string
          title: Type
          description: Identifier of the action the Base44 builder offers for this check.
          example: enable_llms_txt
        label:
          type: string
          title: Label
          description: Label the builder shows on the button for this action.
          example: Enable AI site guide
        params:
          additionalProperties: true
          type: object
          title: Params
          description: >-
            Arguments to pass with the action. Empty on every action a scan
            emits today; keep reading it rather than assuming it stays empty.
          example: {}
      type: object
      required:
        - type
        - label
      title: SEOFixAction
      description: The remedy Base44 offers for a check, for display.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: api_key
      description: Personal API key.

````