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

# Estimate a Plot query before running it

> Plans the SQL exactly as a Plot preview would run it and runs nothing. inputBytes counts the files the plan reads after partition pruning and before file-statistics pruning, so it is an upper bound on what the query reads; unmeasuredLeaves above 0 means the counts are a lower bound for some sources. computeCost prices the bytes at the platform rate; its CPU-time price and elapsedMs stay null until a model for that kind of query is measured. Each aiCallSites entry is one AI-call node with maxCalls, an upper bound on its paid calls: ad-hoc SQL reads no inference log, so each row that reaches the node is one call. The counts include rows that Policies hide from the caller.

## Access

This operation needs a Parable token. An MCP token cannot call it. See [Authentication](/getting-started/authentication.md) for the kinds of token.

Your roles must grant one of `tenant.view`, `tenant.data.query.providers`.

## Errors

* `HTTP 400`: The SQL, query parameters, or reading is not valid, or Parable cannot plan the SQL. The message gives the reason, and the code is a QL code. Correct the query using the stated reason.
* `HTTP 403`: The caller may not run this SQL, under the same rules as a Plot preview (PA-AU-019). Use an identity that may run the SQL, or estimate only what you may read.
* `HTTP 404`: The requested Parable ref or published pin is not available in this Workspace. Check the ref or commit ID and Workspace context.


## OpenAPI

````yaml /api-reference/openapi.json post /api/parables/immediate-plot-query/actions/estimate
openapi: 3.0.3
info:
  description: >-
    Use the Workspace API to work with artifacts, connectors, Parables, and
    account resources.
  title: Workspace API
  version: v2026.10.22.1
servers: []
security: []
paths:
  /api/parables/immediate-plot-query/actions/estimate:
    post:
      tags:
        - Parables
      summary: Estimate a Plot query before running it
      description: >-
        Plans the SQL exactly as a Plot preview would run it and runs nothing.
        inputBytes counts the files the plan reads after partition pruning and
        before file-statistics pruning, so it is an upper bound on what the
        query reads; unmeasuredLeaves above 0 means the counts are a lower bound
        for some sources. computeCost prices the bytes at the platform rate; its
        CPU-time price and elapsedMs stay null until a model for that kind of
        query is measured. Each aiCallSites entry is one AI-call node with
        maxCalls, an upper bound on its paid calls: ad-hoc SQL reads no
        inference log, so each row that reaches the node is one call. The counts
        include rows that Policies hide from the caller.
      operationId: ParablesEstimateImmediatePlotQueryHandler
      parameters:
        - description: >-
            Workspace slug. Overrides subdomain-based workspace resolution when
            provided.
          in: header
          name: X-Tenant
          required: false
          schema:
            example: acme
            type: string
        - description: Optional request correlation ID echoed back in the response.
          in: header
          name: X-Request-ID
          required: false
          schema:
            example: 550e8400-e29b-41d4-a716-446655440000
            format: uuid
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ImmediatePlotQueryInput'
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  data:
                    $ref: '#/components/schemas/SqlEstimate'
                  links:
                    $ref: '#/components/schemas/ResponseLinks'
                  meta:
                    $ref: '#/components/schemas/ResponseMeta'
                required:
                  - data
                  - meta
                type: object
          description: Successful response
          headers:
            X-Request-ID:
              $ref: '#/components/headers/X-Request-ID'
        '400':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: >-
            The request is not valid. `detail` says why. A value that breaks a
            field rule gives the code `WA-VL-001` and an `errors` member that
            names each field.
          headers:
            X-Request-ID:
              $ref: '#/components/headers/X-Request-ID'
        '401':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: The token is missing, malformed, revoked, or expired.
          headers:
            X-Request-ID:
              $ref: '#/components/headers/X-Request-ID'
        '403':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: >-
            The token cannot make this request. For example, the roles of its
            owner do not allow the operation.
          headers:
            X-Request-ID:
              $ref: '#/components/headers/X-Request-ID'
        '413':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: The request body is larger than 1 MB.
          headers:
            X-Request-ID:
              $ref: '#/components/headers/X-Request-ID'
        '429':
          content:
            text/plain:
              schema:
                example: Too Many Requests
                type: string
          description: >-
            More than 120 requests a minute came from your address. Wait for the
            number of seconds in `Retry-After`, then try again.
          headers:
            Retry-After:
              description: The number of seconds to wait before you try again.
              schema:
                type: integer
            X-Request-ID:
              $ref: '#/components/headers/X-Request-ID'
        '500':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: The request failed on the server.
          headers:
            X-Request-ID:
              $ref: '#/components/headers/X-Request-ID'
        '504':
          content:
            text/plain:
              schema:
                example: Gateway Timeout
                type: string
          description: The operation did not finish in 30 seconds.
          headers:
            X-Request-ID:
              $ref: '#/components/headers/X-Request-ID'
      security:
        - bearerAuth: []
      x-codeSamples:
        - label: TypeScript SDK
          lang: typescript
          source: >-
            import { WebApiSDK } from "@parable-platform/web-api-sdk";

            import type { ImmediatePlotQueryInput } from
            "@parable-platform/web-api-types";


            const sdk = new WebApiSDK({
              baseUrl: process.env.PARABLE_API_URL!,
              auth: { token: process.env.PARABLE_API_TOKEN! },
              requestInterceptor: (config) => ({
                ...config,
                headers: { ...config.headers, "X-Tenant": process.env.PARABLE_WORKSPACE! },
              }),
            });


            const input: ImmediatePlotQueryInput = JSON.parse(`{
              "sql": "example"
            }`);

            const result = await sdk.parables.estimateImmediatePlotQuery(
              input
            );
        - label: Python SDK
          lang: python
          source: |-
            import json
            import os

            from parable_types_web_api import ImmediatePlotQueryInput
            from parable_web_api_sdk import ClientConfig, WebApiSDK

            sdk = WebApiSDK(
                ClientConfig(
                    base_url=os.environ["PARABLE_API_URL"],
                    auth_token=os.environ["PARABLE_API_TOKEN"],
                    default_headers={"X-Tenant": os.environ["PARABLE_WORKSPACE"]},
                )
            )

            input_data = ImmediatePlotQueryInput.model_validate(
                json.loads("{\"sql\":\"example\"}"),
            )
            result = sdk.parables.estimate_immediate_plot_query(input_data)
        - label: Rust SDK
          lang: rust
          source: >-
            use parable_web_api_sdk::{types, ClientConfig, RequestOptions,
            WebApiSdk};


            let sdk = WebApiSdk::new(ClientConfig::with_base_url(
                std::env::var("PARABLE_API_URL")?,
                Some(std::env::var("PARABLE_API_TOKEN")?),
                None,
            ))?;

            let options = RequestOptions::with_header("X-Tenant",
            std::env::var("PARABLE_WORKSPACE")?, None, None);


            let input: types::ImmediatePlotQueryInput = serde_json::from_value(
                serde_json::json!({
              "sql": "example"
            }),

            )?;

            let result = sdk.parables.estimate_immediate_plot_query(
                input,
                Some(&options)
            ).await?;
        - label: Go SDK
          lang: go
          source: "sdk, err := webapisdk.New(webapisdk.SDKConfig{\n\tBaseURL: os.Getenv(\"PARABLE_API_URL\"),\n\tAuth:    &webapisdk.AuthConfig{Token: os.Getenv(\"PARABLE_API_TOKEN\")},\n\tRequestInterceptor: func(ctx context.Context, req *http.Request) error {\n\t\treq.Header.Set(\"X-Tenant\", os.Getenv(\"PARABLE_WORKSPACE\"))\n\t\treturn nil\n\t},\n})\nif err != nil {\n\treturn err\n}\n\nvar input webapitypes.ImmediatePlotQueryInput\nif err := json.Unmarshal([]byte(`{\"sql\":\"example\"}`), &input); err != nil {\n\treturn err\n}\nresult, err := sdk.ParablesNamespace.EstimateImmediatePlotQuery(ctx, input)"
components:
  schemas:
    ImmediatePlotQueryInput:
      description: >-
        One SQL statement to run once against the Workspace's data.

        The operation caps no rows. The result is every row the statement
        returns,

        up to the query row limit, which fails the query rather than truncating
        it

        (QL-SQ-005). To read fewer rows, write a LIMIT in the SQL. A reader may
        also

        stop reading early: the Parable app stops at its display limit and an
        MCP

        tool call at 3 MiB of Arrow.
      properties:
        commitId:
          description: UUID v4 with automatic base62 encoding for client-facing APIs
          nullable: true
          pattern: >-
            ^([0-9A-Za-z]{1,22}|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$
          type: string
        expectedRefRevision:
          description: >-
            Draft definition revision observed by the React host. Requires
            refId;

            execution refuses a stale revision instead of relabeling old code as
            new.

            Omitted means the caller deliberately requests the latest authorized
            ref.
          format: int64
          maximum: 9007199254740991
          minimum: 1
          nullable: true
          type: integer
        jev:
          description: >-
            Jev defaults for this query only. SQL model arguments override the
            default.

            The null policy applies to recoverable input, provider and response
            failures.
          oneOf:
            - $ref: '#/components/schemas/JevSessionOptions'
            - enum:
                - null
              nullable: true
              type: object
        parameters:
          description: >-
            Positional values for $1, $2, ... placeholders, sent through Flight
            SQL

            prepared-statement binding. Values are never interpolated into SQL.

            Each value is a JSON scalar (string, finite number, boolean, or
            null).

            Omitted/null/empty uses the ordinary unparameterized query path.
          items:
            description: 'Any valid JSON value: object, array, primitive, or null'
          nullable: true
          type: array
        reading:
          description: >-
            Host-selected data reading. Omitted raw SQL requests Current; a
            bound React

            Plot always sends its canonical context.data. Unsupported Report
            execution

            is refused explicitly rather than silently relabeled as Current.
          oneOf:
            - $ref: '#/components/schemas/PlotDataReading'
            - enum:
                - null
              nullable: true
              type: object
        refId:
          description: >-
            Where the Plot runs. At most one of the two: `refId` is the ref
            being

            viewed (a Proposal draft, or a Personal-mode root), `commitId` is a

            published line's pin. Parable uses them to choose the Parable-scope
            rows

            that `workspace.preferences` serves. Both absent means the Plot
            reads no

            Preferences.
          nullable: true
          pattern: >-
            ^([0-9A-Za-z]{1,22}|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$
          type: string
        sql:
          description: >-
            Single SQL statement. Text.Sql rejects empty; maxLength is 100000
            bytes.
          maxLength: 100000
          minLength: 1
          pattern: ^[\s\S]*$
          type: string
      required:
        - sql
      type: object
    SqlEstimate:
      description: The answer to a SQL dry run.
      properties:
        aiCallLimits:
          description: >-
            The AI-call limits the statement would run under. Null when Parable
            does

            not report them.
          oneOf:
            - $ref: '#/components/schemas/SqlAiCallLimits'
            - enum:
                - null
              nullable: true
              type: object
        aiCallSites:
          description: >-
            One per AI-call node, in plan order. Empty when the statement calls
            no AI

            function.
          items:
            $ref: '#/components/schemas/SqlAiCallSite'
          type: array
        computeCost:
          $ref: '#/components/schemas/SqlComputeCost'
        elapsedMs:
          description: >-
            Engine milliseconds: from the first batch to the end of the stream,
            less

            the client's reading time and the wait for a concurrency permit.
            Null

            until a time model passes its error rule for this kind of query.
          oneOf:
            - $ref: '#/components/schemas/SqlEstimateRange'
            - enum:
                - null
              nullable: true
              type: object
        input:
          $ref: '#/components/schemas/SqlInputEstimate'
      required:
        - input
        - computeCost
        - aiCallSites
      type: object
    ResponseLinks:
      properties:
        first:
          nullable: true
          type: string
        last:
          nullable: true
          type: string
        next:
          nullable: true
          type: string
        prev:
          nullable: true
          type: string
      type: object
    ResponseMeta:
      properties:
        requestId:
          description: Unique identifier for the request
          type: string
        totalCount:
          description: Total count of items for paginated responses
          nullable: true
          type: integer
      required:
        - requestId
      type: object
    ProblemDetails:
      description: An error response in the Problem Details format (RFC 9457).
      properties:
        code:
          description: The stable error code, when the problem has one.
          example: WA-VL-001
          type: string
        detail:
          description: What went wrong with this request.
          type: string
        details:
          description: >-
            Structured facts about the refusal, such as the entity or the path
            it concerns. Only some refusals have it.
        errors:
          description: >-
            The fields or filters that failed validation. Only a validation or
            filter error has it.
        requestId:
          description: >-
            The ID of the request, when the problem has one. The `X-Request-ID`
            response header always has it.
          type: string
        status:
          description: The HTTP status code.
          example: 400
          type: integer
        title:
          description: A short summary, usually the text of the HTTP status.
          example: Bad Request
          type: string
        type:
          description: >-
            The problem type. It is `about:blank`: the status and `code`
            identify the problem.
          example: about:blank
          type: string
      required:
        - type
        - title
        - status
        - detail
      type: object
    JevSessionOptions:
      additionalProperties: false
      description: >-
        Query-local controls. Credentials and the endpoint are server
        configuration.

        A null model leaves the choice to the Workspace model policy:

        the Workspace's SQL_JEV default, else the platform default.
      properties:
        model:
          nullable: true
          type: string
        on_error:
          $ref: '#/components/schemas/JevOnError'
      required:
        - on_error
      type: object
    PlotDataReading:
      description: >-
        Requested data reading, shared by React context and the Plot data
        protocol.

        REPORT_RUN requires reportRunId; CURRENT requires null. Boundary
        validation

        enforces this cross-field invariant in every executor.
      properties:
        kind:
          $ref: '#/components/schemas/PlotDataReadingKind'
        reportRunId:
          description: UUID v4 with automatic base62 encoding for client-facing APIs
          nullable: true
          pattern: >-
            ^([0-9A-Za-z]{1,22}|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$
          type: string
      required:
        - kind
      type: object
    SqlAiCallLimits:
      description: >-
        The AI-call limits each query runs under. A query whose aiCallSites'

        maxCalls add up to more than

        maxCalls may stop at the limit with QL-SQ-010. The dry run itself
        refuses

        nothing: maxCalls is an upper bound, and a filter or a LIMIT often keeps

        the real count far lower.
      properties:
        maxCalls:
          description: The most paid AI calls the query may send.
          format: int64
          maximum: 9007199254740991
          minimum: 0
          type: integer
        maxCostCents:
          description: The most AI cost the query may spend, in US cents (QL-SQ-011).
          format: int64
          maximum: 9007199254740991
          minimum: 0
          type: integer
      required:
        - maxCalls
        - maxCostCents
      type: object
    SqlAiCallSite:
      description: |-
        One AI-call node of the plan: ponder(), embed() or a Jev function. Names
        follow QueryRunEstimateSite.
      properties:
        functionNames:
          description: The SQL functions the node calls, such as ponder or embed.
          items:
            type: string
          type: array
        maxCalls:
          description: >-
            The most Provider calls the node can make: an upper bound on the
            rows

            that reach it, from the rows its leaves report and the operators
            between

            them. Ad-hoc SQL reads no inference log, so each row is one call.
            Null

            when no bound can be taken, such as above a leaf that reports no
            rows.

            A number, so a bound of 0 stays apart from no bound.
          format: double
          minimum: 0
          nullable: true
          type: number
      required:
        - functionNames
      type: object
    SqlComputeCost:
      description: >-
        The compute cost the estimate implies at Parable's rates. A figure for a

        person to judge a query by: nothing bills it. US cents with fractions,
        as

        QueryRunEstimate.estimatedCost.
      properties:
        byBytesRead:
          description: >-
            inputBytes, in GiB, times centsPerGibRead. An upper bound, as
            inputBytes

            is.
          format: double
          type: number
        byCpuTime:
          description: >-
            The predicted CPU time, in hours, times centsPerCpuHour. Null until
            a

            CPU-time model passes its error rule for this kind of query.
          oneOf:
            - $ref: '#/components/schemas/SqlEstimateRange'
            - enum:
                - null
              nullable: true
              type: object
        centsPerCpuHour:
          format: double
          type: number
        centsPerGibRead:
          description: The rates the figures used, so a reader can recompute them.
          format: double
          type: number
      required:
        - byBytesRead
        - centsPerGibRead
        - centsPerCpuHour
      type: object
    SqlEstimateRange:
      description: A range a model predicts, and the model that predicted it.
      properties:
        high:
          format: double
          type: number
        low:
          format: double
          type: number
        modelVersion:
          type: string
      required:
        - low
        - high
        - modelVersion
      type: object
    SqlInputEstimate:
      description: >-
        What a statement reads, from its physical plan's leaves before it runs.

        Parable records the same value for every query it runs. Bytes count
        after

        partition pruning and before file-statistics and row-group pruning, so
        they

        bound what the query reads from storage.
      properties:
        inputBytes:
          description: >-
            Bytes of the files the plan's file scans read, or of the byte ranges
            a

            split file is read in.
          format: int64
          maximum: 9007199254740991
          minimum: -9007199254740991
          type: integer
        inputFileCount:
          description: Files the plan's file scans read.
          format: int64
          maximum: 9007199254740991
          minimum: -9007199254740991
          type: integer
        inputRows:
          description: |-
            Rows the plan's other leaves report, such as a Workspace Pool
            projection's batch, plus the rows the file scans' statistics report.
            Null when no leaf reports a row count. A number rather than an Int64
            scalar, so null and 0 stay apart in every language.
          format: double
          minimum: 0
          nullable: true
          type: number
        unmeasuredLeaves:
          description: >-
            Leaves that read no file and report no rows, such as a

            workspace.preferences read. Above 0, inputBytes and inputRows
            undercount.
          format: int64
          maximum: 9007199254740991
          minimum: -9007199254740991
          type: integer
      required:
        - inputBytes
        - inputFileCount
        - unmeasuredLeaves
      type: object
    JevOnError:
      enum:
        - fail
        - 'null'
      type: string
    PlotDataReadingKind:
      description: A live reading or one concrete Report run. Draft previews use CURRENT.
      enum:
        - CURRENT
        - REPORT_RUN
      type: string
  headers:
    X-Request-ID:
      description: >-
        The ID of the request. It is the `X-Request-ID` value that you sent, or
        an ID that the server made. Give it when you report a problem.
      schema:
        type: string
  securitySchemes:
    bearerAuth:
      description: >-
        A personal access token, which starts with `pat_`. A Parable token can
        call each operation that the roles of its owner allow. An MCP token can
        call only the read operations that are open to agents. A SQL token does
        not work on this API.
      scheme: bearer
      type: http

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.