Onto Score API

Run the AIO scorer on a URL — returns 0–100 score, penalties, benefits, recommendations.

Operations 1

POST /v1/score Score any URL on the AIO 0–100 scale #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/buildonto-dev:buildonto-dev-score-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

buildonto-dev-score-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Onto Read Score API
  version: 1.0.0
  summary: Turn any URL into clean Markdown, an AIO accuracy score, and hallucination-risk flags.
  description: 'The Onto Read API takes a URL and returns clean Markdown that an LLM can consume directly,

    plus a 0–100 AIO accuracy score and hallucination-risk metadata.


    **Authentication.** All requests use bearer-token auth — pass your API key in the

    `Authorization` header. Get one at app.buildonto.dev/read/keys;

    the Free tier gives you 1,000 credits a month.


    **Endpoints.** `/v1/read`, `/v1/score`, `/v1/read-and-score` and `/v1/extract` work on one

    URL. `/v1/batch` handles up to 50 URLs in one call, `/v1/map` lists a site''s URLs, and

    `/v1/usage` shows your quota and credits. Most agent workflows want `/v1/read-and-score` —

    it returns both the cleaned Markdown and the accuracy metadata in a single round-trip.


    **Credits.** `/v1/read`, `/v1/read-and-score`, `/v1/extract` and `/v1/map` cost 1 credit per

    call; `/v1/batch` costs 1 credit per URL. `/v1/score` and `/v1/usage` are free. Failed

    requests are refunded.


    **Errors.** Every error response shares the same shape with a stable `code` field — branch

    on `code`, not on the human-readable `message`. Extra details, like `retry_after`, sit at

    the top level of the body. See

    the error codes index for the full catalog.'
  contact:
    name: Onto support
    email: founder@buildonto.dev
    url: https://buildonto.dev
  license:
    name: Proprietary
    url: https://buildonto.dev/legal/terms
servers:
- url: https://api.buildonto.dev
  description: Production
security:
- bearerAuth: []
tags:
- name: Score
  description: Run the AIO scorer on a URL — returns 0–100 score, penalties, benefits, recommendations.
paths:
  /v1/score:
    post:
      tags:
      - Score
      summary: Score any URL on the AIO 0–100 scale
      operationId: scoreUrl
      description: 'Runs the AIO scoring engine on the target URL. Returns the 0–100 score, the letter

        grade, hallucination risk, and the full diagnostic shape (penalties, benefits,

        recommendations, insights, and a Markdown bot-preview).


        The scoring model is subtractive — every URL starts at 100 and loses points for

        specific structural issues. See the AIO score concept

        for the full penalty table.


        Free: it doesn''t use credits, so it never returns `PAYMENT_REQUIRED` or `RATE_LIMITED`.

        For a PDF or plain-text URL, `aio_score`, `grade` and `hallucination_risk` are null,

        `insights` is `{ pdf_text_extracted: true }`, and a `note` explains why.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ScoreRequest'
            example:
              url: https://resend.com/pricing
      responses:
        '200':
          description: Successful score
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScoreResponse'
              example:
                status: success
                url: https://resend.com/pricing
                aio_score: 87
                grade: Good
                hallucination_risk: low
                insights:
                  robots_allowed: true
                  waf_blocked: false
                  markdown_supported: true
                  json_ld_present: true
                  semantic_hierarchy: true
                penalties: []
                benefits:
                - 'Native Markdown Support: This site natively serves Markdown to agents.'
                - 'Metadata Extraction: Onto distills explicit JSON-LD schema.'
                recommendations:
                - title: Migrate to Onto SDK
                  description: Native Markdown detected but lacks agent analytics. Upgrade to the Onto layer.
                  priority: Medium
                stats:
                  raw_size: 412KB
                  efficiency: 94.2%
                  extraction_time_ms: 980
                bot_preview: '# Resend Pricing


                  > Build, test, and deliver…


                  ## Plans

                  ### Free

                  ### Pro'
        '400':
          $ref: '#/components/responses/InvalidUrl'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/UrlNotFound'
        '413':
          $ref: '#/components/responses/TooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedType'
        '422':
          $ref: '#/components/responses/ImagePdf'
        '429':
          $ref: '#/components/responses/ConcurrentLimit'
        '500':
          $ref: '#/components/responses/ExtractionFailed'
        '502':
          $ref: '#/components/responses/TlsError'
        '504':
          $ref: '#/components/responses/Timeout'
components:
  responses:
    UnsupportedType:
      description: '`UNSUPPORTED_TYPE` — the URL isn''t HTML, PDF or text (for example an image, video or archive).'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: error
            code: UNSUPPORTED_TYPE
            message: Not a readable document (image/png). Onto reads HTML, PDF, and text formats.
    Forbidden:
      description: 'Multiple causes:

        - `WAF_BLOCKED` — target site refused our crawler (origin returned 401/403)

        - `ROBOTS_BLOCKED` — target site''s robots.txt has `Disallow: /` for GPTBot or `*`

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            waf:
              value:
                status: error
                code: WAF_BLOCKED
                message: Target site refused our crawler (HTTP 403)
            robots:
              value:
                status: error
                code: ROBOTS_BLOCKED
                message: Target site's robots.txt disallows AI crawlers. We honor robots.txt — see https://buildonto.dev/about-bot.
    Timeout:
      description: Target site took longer than 15 seconds to respond.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: error
            code: TIMEOUT
            message: Target site took too long to respond
    UrlNotFound:
      description: Target URL returned 404, or its hostname doesn't resolve.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: error
            code: URL_NOT_FOUND
            message: 'Target URL returned 404: https://example.com/old-page'
    TooLarge:
      description: '`TOO_LARGE` — the document is over 10 MB.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: error
            code: TOO_LARGE
            message: Document exceeds the read limit; Onto reads up to 10MB
    ImagePdf:
      description: '`IMAGE_PDF` — a scanned PDF with no text layer. Onto does not OCR.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: error
            code: IMAGE_PDF
            message: This PDF has no extractable text (likely scanned or image-only). Onto does not OCR.
    Unauthorized:
      description: Missing, malformed, or revoked Bearer token.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: error
            code: UNAUTHORIZED
            message: Missing or invalid API key
    InvalidUrl:
      description: '`INVALID_URL` — body isn''t JSON, `url` is missing or unparseable, the scheme isn''t

        http(s), or the URL points at a private or internal host.

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: error
            code: INVALID_URL
            message: '"url" field is required'
    ExtractionFailed:
      description: '`EXTRACTION_FAILED` — the origin returned an error status other than 401/403/404, the

        host refused the connection or was unreachable, there were more than 5 redirects or a

        bad redirect, or the page or PDF couldn''t be parsed. Retry once; errors are never cached.

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: error
            code: EXTRACTION_FAILED
            message: Upstream returned HTTP 503
    TlsError:
      description: '`TLS_ERROR` — the target site''s TLS certificate or handshake failed.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: error
            code: TLS_ERROR
            message: Target site has an invalid TLS certificate (CERT_HAS_EXPIRED)
    ConcurrentLimit:
      description: '`CONCURRENT_LIMIT` — too many requests running at once for your plan (per account, not per key). Retry after 1 second.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: error
            code: CONCURRENT_LIMIT
            message: Too many concurrent requests for your tier (5/5 in flight). Retry in a moment.
            in_flight: 5
            limit: 5
            retry_after: 1
  schemas:
    ScoringInsights:
      type: object
      required:
      - robots_allowed
      - waf_blocked
      - markdown_supported
      - json_ld_present
      - semantic_hierarchy
      properties:
        robots_allowed:
          type: boolean
        waf_blocked:
          type: boolean
        markdown_supported:
          type: boolean
        json_ld_present:
          type: boolean
        semantic_hierarchy:
          type: boolean
    ErrorResponse:
      type: object
      required:
      - status
      - code
      - message
      properties:
        status:
          type: string
          enum:
          - error
        code:
          type: string
          description: Stable error code — branch on this, not on `message`.
        message:
          type: string
          description: Human-readable message; wording may change without notice.
        retry_after:
          oneOf:
          - type: integer
            description: Seconds to wait before retrying (CONCURRENT_LIMIT). Always 1.
          - type: string
            format: date-time
            description: When monthly credits reset — the end of the current UTC month (RATE_LIMITED).
        plan_limit:
          type: integer
          description: Your plan's monthly credits (PAYMENT_REQUIRED).
        credits_remaining:
          type: integer
          description: Bought credits left, always 0 (PAYMENT_REQUIRED).
        in_flight:
          type: integer
          description: Requests running for your account right now (CONCURRENT_LIMIT).
        limit:
          type: integer
          description: Your plan's concurrency limit (CONCURRENT_LIMIT).
    Recommendation:
      type: object
      required:
      - title
      - description
      - priority
      properties:
        title:
          type: string
        description:
          type: string
        priority:
          type: string
          enum:
          - High
          - Medium
          - Low
    ScoreRequest:
      type: object
      required:
      - url
      properties:
        url:
          type: string
          format: uri
          description: Absolute URL to score.
          example: https://resend.com/pricing
    ScoreResponse:
      type: object
      required:
      - status
      - url
      - aio_score
      - grade
      - hallucination_risk
      - insights
      - penalties
      - benefits
      - recommendations
      - stats
      - bot_preview
      properties:
        status:
          type: string
          enum:
          - success
        url:
          type: string
          format: uri
        aio_score:
          type:
          - integer
          - 'null'
          minimum: 0
          maximum: 100
          description: 0–100 subtractive accuracy score. See `/concepts/aio-score` for the model. Null for PDF and plain-text URLs.
        grade:
          type:
          - string
          - 'null'
          enum:
          - Excellent
          - Good
          - Needs work
          - AI-hostile
          - Invisible
          - null
        hallucination_risk:
          type:
          - string
          - 'null'
          enum:
          - low
          - medium
          - high
          - null
        insights:
          $ref: '#/components/schemas/ScoringInsights'
        penalties:
          type: array
          items:
            type: string
          description: Human-readable list of penalties applied to the score.
        benefits:
          type: array
          items:
            type: string
          description: Positive signals detected on the page.
        recommendations:
          type: array
          items:
            $ref: '#/components/schemas/Recommendation'
        stats:
          type: object
          properties:
            raw_size:
              type: string
              description: Human-readable original HTML size (e.g. `"412KB"`).
            efficiency:
              type: string
              description: Human-readable signal-to-noise ratio (e.g. `"94.2%"`).
            extraction_time_ms:
              type: integer
        bot_preview:
          type: string
          description: Auto-generated Markdown preview with normalized heading hierarchy.
        note:
          type: string
          description: Sent only for PDF and plain-text URLs, to explain why the score fields are null.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: onto_sk_live_…
      description: 'Pass your API key as `Authorization: Bearer {your_key}`. Get a key at

        [app.buildonto.dev/read/keys](https://app.buildonto.dev/read/keys).

        '