Onto Read API

Fetch and clean public URLs into agent-ready Markdown.

Operations 1

POST /v1/read Read any URL as clean Markdown #

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-read-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-read-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Onto Read 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: Read
  description: Fetch and clean public URLs into agent-ready Markdown.
paths:
  /v1/read:
    post:
      tags:
      - Read
      summary: Read any URL as clean Markdown
      operationId: readUrl
      description: 'Fetches the target URL, strips chrome, and returns the semantic content as Markdown.

        Costs 1 credit. Responses are cached for one hour — pass `"fresh": true` to bypass the

        cache and force a re-fetch (still subject to your monthly quota).


        Set `Accept: text/markdown` to receive the raw Markdown as the response body instead

        of the JSON wrapper.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReadRequest'
            examples:
              basic:
                summary: Read a public URL
                value:
                  url: https://stripe.com/pricing
              fresh:
                summary: Bypass the cache
                value:
                  url: https://stripe.com/pricing
                  fresh: true
      responses:
        '200':
          description: Successful read
          headers:
            X-Onto-Cache:
              description: HIT or MISS
              schema:
                type: string
                enum:
                - HIT
                - MISS
            X-RateLimit-Remaining:
              description: Plan credits remaining this month
              schema:
                type: integer
            X-RateLimit-Reset:
              description: ISO timestamp of the next reset (start of the next UTC month)
              schema:
                type: string
                format: date-time
            X-Onto-Billed:
              description: Which budget the call was billed against
              schema:
                type: string
                enum:
                - plan
                - credit
            X-Concurrent-Remaining:
              description: Concurrent slots remaining for your tier
              schema:
                type: integer
            X-Credits-Remaining:
              description: Credit balance after this request. Sent only when the request was billed to credits.
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReadResponse'
              example:
                status: success
                url: https://stripe.com/pricing
                markdown: '# Stripe Pricing


                  > Integrated per-transaction pricing…'
                metadata:
                  title: Stripe Pricing
                  description: Integrated per-transaction pricing with no setup fees or monthly fees.
                  language: en
                stats:
                  raw_html_size_kb: 612.4
                  markdown_size_kb: 14.2
                  reduction_percent: 97.7
                  extraction_time_ms: 1230
                cache:
                  hit: false
                  ttl_seconds: 3600
            text/markdown:
              schema:
                type: string
              example: '# Stripe Pricing


                > Integrated per-transaction pricing…'
        '400':
          $ref: '#/components/responses/InvalidUrl'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '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/RateLimited'
        '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'
    PaymentRequired:
      description: Paid plans only — monthly plan credits AND bought credits are both used up. Free gets `RATE_LIMITED` instead.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            status: error
            code: PAYMENT_REQUIRED
            message: Monthly plan quota exceeded and credit balance is empty. Top up credits in the dashboard to continue.
            plan_limit: 10000
            credits_remaining: 0
    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)
    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
    RateLimited:
      description: 'Multiple causes:

        - `RATE_LIMITED` — monthly plan credits used up (Free only; paid plans spill into bought credits)

        - `CONCURRENT_LIMIT` — too many requests running at once for your plan; retry after 1 second

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            monthly:
              value:
                status: error
                code: RATE_LIMITED
                message: Monthly quota exceeded
                retry_after: '2026-10-01T00:00:00.000Z'
            concurrent:
              value:
                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:
    ReadResponse:
      type: object
      required:
      - status
      - url
      - markdown
      - metadata
      - stats
      - cache
      properties:
        status:
          type: string
          enum:
          - success
        url:
          type: string
          format: uri
        markdown:
          type: string
          description: Clean, agent-ready Markdown extracted from the target URL.
        metadata:
          $ref: '#/components/schemas/Metadata'
        stats:
          $ref: '#/components/schemas/ReadStats'
        warnings:
          type: array
          items:
            type: string
          description: 'Sent by `/v1/read` only when there is something to flag — for example, a page that

            yielded very little text and most likely renders its content with JavaScript, which

            Onto does not execute.

            '
        cache:
          $ref: '#/components/schemas/CacheInfo'
    CacheInfo:
      type: object
      required:
      - hit
      - ttl_seconds
      properties:
        hit:
          type: boolean
        ttl_seconds:
          type: integer
          description: Cache TTL applied to this response (currently 3600).
    ReadStats:
      type: object
      required:
      - raw_html_size_kb
      - markdown_size_kb
      - reduction_percent
      - extraction_time_ms
      properties:
        raw_html_size_kb:
          type: number
        markdown_size_kb:
          type: number
        reduction_percent:
          type: number
          description: How much smaller the Markdown is vs. the raw HTML (0–100).
        extraction_time_ms:
          type: integer
    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).
    ReadRequest:
      type: object
      required:
      - url
      properties:
        url:
          type: string
          format: uri
          description: Absolute URL to read. Must include scheme (`http://` or `https://`).
          example: https://stripe.com/pricing
        fresh:
          type: boolean
          default: false
          description: Bypass the one-hour cache and re-fetch from origin.
    Metadata:
      type: object
      properties:
        title:
          type: string
        description:
          type: string
        language:
          type: string
          description: ISO 639-1 language code if detected.
  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).

        '