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

> ## Agent Instructions
> Agents: start with https://docs.pre.dev/agents.md, which has complete recipes, plan access, polling rules, errors and limits.
> Authenticate with the workspace API key (pdk_…) from Integrations → Built-in, sent as Authorization: Bearer <key>.
> REST API: https://api.pre.dev (OpenAPI: https://docs.pre.dev/api-reference/openapi.json). AI Gateway: https://api.pre.dev/v1, OpenAI-compatible (OpenAPI: https://docs.pre.dev/api-reference/ai-gateway.openapi.json).
> pre.dev MCP server: npx -y @predotdev/mcp setup adds it to every local coding agent with every pre.dev tool plus the user's own Chrome; the hosted server is https://api.pre.dev/mcp. Search these docs over MCP at https://docs.pre.dev/mcp.

# Run browser tasks

> Submit a task array and receive JSON, an async run ID, or a live SSE stream.

Always pass a `tasks` array, including for a single task. Each request creates one run, called a batch in response types. Send your key as described in [authentication](https://predev.mintlify.site/api-reference/authentication.md#send-the-key).

## Task controls

Every task needs a full HTTP(S) `url` and either a nonblank `instruction` or a non-empty `output` JSON Schema object. String-valued `input` is optional and does not replace instructions. Choose `mode: "extract"` for schema-oriented reads or `mode: "agent"` for goal-driven navigation; `auto` lets the service choose.

`maxSteps` and `maxDurationSeconds` set per-task budgets. For compatibility with MCP, use `1`–`50` steps and `5`–`600` seconds. REST also accepts the older `maxIterations` and `timeoutMs` names; those take precedence if both forms are sent. Do not send both forms in new code. Defaults vary with the task and execution path, so set an explicit budget when your workflow depends on one.

```json theme={null}
{
  "tasks": [{
    "url": "https://example.com",
    "instruction": "Extract the main page heading.",
    "output": {
      "type": "object",
      "properties": { "heading": { "type": "string" } },
      "required": ["heading"]
    },
    "mode": "extract",
    "maxSteps": 10,
    "maxDurationSeconds": 120
  }],
  "concurrency": 5,
  "async": true
}
```

When `output` is omitted, schema inference may be attempted. Supply your own schema for a predictable contract; a successful task does not always have `data`.

## Input validation

The service validates the entire batch before charging credits, creating a run, or queueing browser work. Malformed URLs, missing instructions without an output schema, and domains with no DNS address return an actionable error with a `code`, a zero-based `taskIndex`, and a `field` when applicable.

DNS verification has a five-second request budget. Temporary DNS failures return `503` with `URL_CHECK_UNAVAILABLE`; retry the submission later. No tasks are started when verification fails. This check verifies the hostname, not page availability: authenticated pages and bot-protected sites remain eligible for browser execution.

The dashboard applies the same task and URL format checks before enabling Run. CSV instructions are checked after row-variable interpolation. Output-schema-only extraction remains supported through the API and MCP.

## Synchronous

With neither `async` nor `stream` enabled, the connection waits for a `BatchResult`. Use this for short tasks. The [quickstart](https://predev.mintlify.site/browser-agents/quickstart.md) contains complete curl and SDK examples.

## Async

With `async: true`, HTTP `200` returns an initial result such as:

```json theme={null}
{
  "id": "507f1f77bcf86cd799439011",
  "total": 1,
  "completed": 0,
  "results": [],
  "totalCreditsUsed": 0,
  "status": "processing"
}
```

Save **`id`**, then [poll the run](https://predev.mintlify.site/browser-agents/api/task-status.md) or [attach an SSE stream](https://predev.mintlify.site/browser-agents/api/stream-task.md). `totalCreditsUsed: 0` on this initial response does not mean no credit floor was charged at submission.

## Streaming

With `stream: true`, the response is `text/event-stream`. If both `stream` and `async` are true, **streaming takes precedence**; the server does not reject that combination. Send only your intended mode for clarity.

```bash theme={null}
curl --fail-with-body -N https://api.pre.dev/browser-agent \
  -H "Authorization: Bearer $PREDEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tasks": [{"url":"https://example.com","instruction":"Read the main heading."}],
    "stream": true
  }'
```

| SSE event | `data` payload |
| - | - |
| `task_event` | `{ taskIndex, type, timestamp, iteration?, data }` |
| `task_result` | `{ taskIndex, ...taskResult }` |
| `done` | Full `BatchResult` |
| `error` | `{ error, code?, actionUrl?, taskIndex?, field? }` |

Lines beginning with `:` are keepalive comments. The task index is zero-based. Individual task `done` events inside `task_event` do not finish the batch; wait for the outer SSE `done` event and inspect all task statuses.

A submission stream does not provide the initial `snapshot` event or reliably expose a run ID before completion. Use async submission followed by an [existing-run stream](https://predev.mintlify.site/browser-agents/api/stream-task.md) when you need a saved ID for recovery. Disconnecting does not cancel the work.

## Billing and limits

Every browser task is charged a **0.1-credit floor** when you submit it, so you need available credits to start. When the task finishes, it is charged any usage above the floor, whether it succeeded or not. Only an `ERROR` or `EXHAUSTED` task keeps the floor and nothing more.

The floor is refunded only when a submission fails before its tasks are queued. Each task's `creditsUsed` is its full charge, including the floor, and `totalCreditsUsed` is the sum for the run. Treat both fields as recorded charges, not a forecast or a reserved amount. Free accounts have a limited browser trial budget in addition to the account credit balance; an exhausted trial returns `SUBSCRIPTION_REQUIRED`. See [plans, credits, and pricing](https://predev.mintlify.site/coding-agent/plans-and-credits.md) for what a credit is worth.

| Limit | Value |
| - | - |
| Browser tasks per request | 1,000 |
| Browser tasks running at once within a run (`concurrency`) | 1 to 20, default 5 |
| Browser tasks in flight per account, queued or running | Set by your plan; read `cap` from [`GET /browser-agent-status`](https://predev.mintlify.site/browser-agents/api/queue-status) |
| AI Gateway requests per workspace | 600 a minute; 30 a minute on the Free plan. Catalog reads do not count. |

Your account's in-flight cap can be much smaller than the per-request limit, and it counts every submitted task, including queued work. `concurrency` controls parallelism within the run; it does not let you exceed your account limit.

Use [queue status](https://predev.mintlify.site/browser-agents/api/queue-status.md) to size batches and [errors](https://predev.mintlify.site/api-reference/errors.md) to handle `QUEUE_FULL`, `RATE_LIMITED`, and billing responses. Capacity snapshots are advisory; another submission can consume capacity before yours arrives.

## Idempotency

Send an `Idempotency-Key` header, or `idempotencyKey` in the JSON body, to recover an existing run from the last **24 hours**. The header takes precedence. Use a unique key per logical submission and keep the payload stable across retries.

A matching existing run returns HTTP `200` as **JSON**, including when `stream: true` was requested. Check the response content type. The current SDK methods and MCP tool do not expose this option; use direct REST when needed. See [retry semantics](https://predev.mintlify.site/api-reference/errors.md#retry-submissions).

## Response schemas

The generated schema below describes the run and task fields. [Get run status](https://predev.mintlify.site/browser-agents/api/task-status.md) explains pending entries, task outcomes, retry counts, and timelines. The `completed` count alone is not a reliable completion signal; use batch `status`.

## Errors

| Status | When |
| - | - |
| `400` | `INVALID_TASK`, `INVALID_URL`, `MISSING_INSTRUCTION`, `URL_UNREACHABLE`, or `BATCH_TOO_LARGE`. Nothing was started or charged |
| `401` | Missing or invalid key |
| `402` | `SUBSCRIPTION_REQUIRED` or `INSUFFICIENT_CREDITS`, with an `actionUrl` |
| `429` | `RATE_LIMITED` or `QUEUE_FULL` |
| `500` | Unexpected error. Retrieve the run before resubmitting |
| `503` | `URL_CHECK_UNAVAILABLE` (retry later), or streaming capacity is full (submit with `async: true` and poll) |

Each status has an example body in the response examples. See [errors and retries](https://predev.mintlify.site/api-reference/errors.md).


## OpenAPI

````yaml api-reference/openapi.json POST /browser-agent
openapi: 3.1.0
info:
  title: pre.dev API
  description: >-
    Public REST API for software specifications, browser automation, and credit
    balance. Base URL: https://api.pre.dev. MCP is documented separately at
    https://docs.pre.dev/mcp/tools.
  version: 1.1.0
  contact:
    name: pre.dev Support
    url: https://pre.dev
    email: support@pre.dev
  license:
    name: Proprietary
    url: https://pre.dev/terms
servers:
  - url: https://api.pre.dev
    description: Production API Server
security:
  - apiKeyAuth: []
  - xApiKey: []
tags:
  - name: Specifications
  - name: Browser Agents
  - name: Account
paths:
  /browser-agent:
    post:
      tags:
        - Browser Agents
      summary: Run browser tasks
      description: >-
        Default: synchronous JSON. async=true returns an ID. stream=true takes
        precedence and returns SSE task_event, task_result, done (full result),
        or error. A matching retry key returns JSON even when stream=true. A
        disconnected client does not cancel execution.
      operationId: runTask
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          schema:
            type: string
          description: >-
            Optional user-scoped 24-hour retry lookup. Reuse only for the same
            logical submission; concurrent first requests can still duplicate
            work.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchRequest'
            example:
              tasks:
                - url: https://example.com
                  instruction: Extract the main page heading.
                  output:
                    type: object
                    properties:
                      heading:
                        type: string
                    required:
                      - heading
                  mode: extract
                  maxSteps: 10
                  maxDurationSeconds: 60
              async: true
      responses:
        '200':
          description: >-
            Batch result, asynchronous ID, or SSE stream. Inspect Content-Type
            before parsing. A done batch can contain failed tasks; inspect each
            task status.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BatchResult'
              examples:
                submitted:
                  summary: Async submission
                  value:
                    id: 507f1f77bcf86cd799439011
                    total: 1
                    completed: 0
                    results: []
                    totalCreditsUsed: 0
                    status: processing
                completed:
                  summary: Completed run (illustrative)
                  value:
                    id: 507f1f77bcf86cd799439011
                    total: 1
                    completed: 1
                    results:
                      - url: https://example.com
                        status: SUCCESS
                        data:
                          heading: Example Domain
                        creditsUsed: 0.1
                    totalCreditsUsed: 0.1
                    status: completed
            text/event-stream:
              schema:
                type: string
              example: >+
                event: task_event

                data:
                {"taskIndex":0,"type":"navigation","timestamp":1788739200000,"data":{"url":"https://example.com"}}


                event: task_result

                data: {"taskIndex":0,"status":"SUCCESS","data":{"title":"Example
                Domain"},"creditsUsed":0.1}


                event: done

                data:
                {"id":"64b7f3e1c2a9d5e6f8a01234","status":"completed","total":1,"completed":1,"results":[{"status":"SUCCESS","data":{"title":"Example
                Domain"},"creditsUsed":0.1}],"totalCreditsUsed":0.1}

        '400':
          description: >-
            Invalid task input, malformed or unresolvable URL, missing
            instructions/output schema, or oversized batch. No work is started.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                missing_instruction:
                  summary: Task has no instruction or output schema
                  value:
                    error: >-
                      Task 1: Describe what the agent should do, or provide an
                      output schema.
                    code: MISSING_INSTRUCTION
                    taskIndex: 0
                    field: instruction
                invalid_url:
                  summary: Task URL rejected
                  value:
                    error: >-
                      Task 1: Enter a full website URL, such as
                      https://example.com.
                    code: INVALID_URL
                    taskIndex: 0
                    field: url
                batch_too_large:
                  summary: Too many tasks
                  value:
                    error: A batch can contain at most 1000 tasks.
                    code: BATCH_TOO_LARGE
                url_unreachable:
                  summary: Domain has no DNS address
                  value:
                    error: >-
                      Task 1: We couldn't find the website example.invalid.
                      Check the URL and try again. No tasks were started.
                    code: URL_UNREACHABLE
                    taskIndex: 0
                    field: url
        '401':
          description: Missing, invalid, or ineligible authentication.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                missing_key:
                  summary: No key sent
                  value:
                    error: Missing API key
                    message: >-
                      Please provide an API key via Authorization: Bearer
                      <token> header or x-api-key header
                invalid_key:
                  summary: Unknown key
                  value:
                    error: Authentication failed
                    message: Invalid API key
        '402':
          description: Insufficient balance or a billing gate.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                insufficient_credits:
                  summary: Not enough credits
                  value:
                    error: Need ~0.5 credits, have 0.00. Buy more to continue.
                    code: INSUFFICIENT_CREDITS
                    actionUrl: https://pre.dev/projects/browser-agents?upgrade=credits
                subscription_required:
                  summary: Free allowance used
                  value:
                    error: >-
                      Free accounts can spend up to 2 credits on browser agents.
                      You've used 2.00. Subscribe to keep going.
                    code: SUBSCRIPTION_REQUIRED
                    actionUrl: https://pre.dev/projects/browser-agents?upgrade=subscribe
        '429':
          description: Rate or inflight limit reached.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: 'Rate limit: 600 requests/minute. Try again in a minute.'
                code: RATE_LIMITED
        '500':
          description: Request failed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: Internal server error
        '503':
          description: >-
            Streaming capacity unavailable, or temporary DNS verification
            failure (URL_CHECK_UNAVAILABLE). No tasks start when input
            verification fails.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                url_check_unavailable:
                  summary: Website verification unavailable
                  value:
                    error: >-
                      Website verification timed out or is temporarily
                      unavailable. Please try again. No tasks were started.
                    code: URL_CHECK_UNAVAILABLE
                    field: url
                stream_capacity:
                  summary: 'Streaming capacity full (stream: true)'
                  value:
                    error: >-
                      SSE capacity exceeded on this pod (5000/5000). Retry with
                      async=true and poll GET /browser-agent/:id.
      x-codeSamples:
        - lang: cURL
          source: |-
            curl --fail-with-body https://api.pre.dev/browser-agent \
              -H "Authorization: Bearer $PREDEV_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{
              "tasks": [
                {
                  "url": "https://example.com",
                  "instruction": "Extract the main page heading.",
                  "output": {
                    "type": "object",
                    "properties": {
                      "heading": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "heading"
                    ]
                  },
                  "mode": "extract",
                  "maxSteps": 10,
                  "maxDurationSeconds": 60
                }
              ],
              "async": true
            }'
components:
  schemas:
    BatchRequest:
      type: object
      required:
        - tasks
      properties:
        tasks:
          type: array
          items:
            $ref: '#/components/schemas/Task'
          minItems: 1
          description: >-
            One or more tasks. Default server limit is 1,000, configurable by
            deployment; BATCH_TOO_LARGE reports the active limit.
        concurrency:
          type: integer
          description: >-
            Requested parallelism; defaults to 5 and is clamped to 1–20. Account
            and service capacity may reduce effective parallelism.
          minimum: 1
          maximum: 20
          default: 5
        async:
          type: boolean
          default: false
          description: Return the batch ID immediately, then poll or attach to its stream.
        stream:
          type: boolean
          default: false
          description: Return SSE; takes precedence over async.
        idempotencyKey:
          type: string
          description: >-
            Optional retry lookup key. Idempotency-Key header takes precedence.
            User-scoped lookup lasts 24 hours; concurrent first requests are not
            atomically deduplicated.
    BatchResult:
      type: object
      required:
        - id
        - status
        - results
      properties:
        id:
          type: string
          description: 24-character record ID.
          pattern: ^[a-fA-F0-9]{24}$
        name:
          anyOf:
            - type: string
            - type: 'null'
        taskNames:
          type: array
          items:
            type: string
        total:
          type: integer
        completed:
          type: integer
          description: >-
            Counts result slots, including pending/running placeholders or
            nulls. Do not use this as the completion signal.
        results:
          type: array
          items:
            anyOf:
              - $ref: '#/components/schemas/TaskResult'
              - type: 'null'
          description: >-
            Ordered by task index. Slots can be null or partial until a result
            arrives.
        totalCreditsUsed:
          type: number
        status:
          type: string
          enum:
            - processing
            - completed
            - failed
        createdAt:
          type: string
          format: date-time
        completedAt:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
        liveEvents:
          type: array
          items:
            type: array
            items:
              $ref: '#/components/schemas/RunnerEvent'
          description: >-
            Per-task events when includeEvents=true and live events are
            available.
        error:
          anyOf:
            - type: string
            - type: 'null'
      additionalProperties: true
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
        message:
          type: string
          description: Optional detail; not returned by every endpoint.
        code:
          type: string
          description: Machine-readable code when available.
        actionUrl:
          type: string
          format: uri
        requiresSubscription:
          type: boolean
        trialsUsed:
          type: integer
        maxTrials:
          type: integer
      additionalProperties: true
    Task:
      type: object
      required:
        - url
      properties:
        url:
          type: string
          description: >-
            Full starting HTTP(S) URL. Domains must resolve to at least one IPv4
            or IPv6 address.
          format: uri
        instruction:
          type: string
          description: Goal and constraints for the task.
        input:
          type: object
          properties: {}
          additionalProperties:
            type: string
          description: Named string inputs available to the runner.
        output:
          type: object
          properties: {}
          additionalProperties: true
          description: >-
            JSON Schema for extracted data. If omitted, the runner may infer a
            schema.
          minProperties: 1
        successCondition:
          type: string
          description: Natural-language completion check.
        mode:
          type: string
          description: >-
            auto chooses extraction when an output schema is provided; agent
            handles interaction.
          enum:
            - auto
            - extract
            - agent
          default: auto
        maxSteps:
          type: integer
          description: >-
            Planning iteration budget. MCP accepts 1–50; use this range across
            clients. REST forwards the value without equivalent range
            validation.
          minimum: 1
          maximum: 50
        maxDurationSeconds:
          type: integer
          description: >-
            Wall-clock budget in seconds. MCP accepts 5–600; use this range
            across clients. Defaults vary by execution path.
          minimum: 5
          maximum: 600
        maxIterations:
          type: integer
          description: >-
            Legacy alias of maxSteps. If both are supplied, maxIterations takes
            precedence.
          deprecated: true
        timeoutMs:
          type: integer
          description: >-
            Legacy millisecond budget. If both are supplied, timeoutMs takes
            precedence over maxDurationSeconds.
          deprecated: true
      description: >-
        A full HTTP(S) URL and either nonblank instructions or a non-empty
        output schema are required. The entire batch is validated before billing
        or queueing.
      anyOf:
        - required:
            - instruction
          properties:
            instruction:
              type: string
              pattern: \S
        - required:
            - output
    TaskResult:
      type: object
      properties:
        url:
          type: string
        name:
          type: string
        instruction:
          type: string
        input:
          type: object
          properties: {}
          additionalProperties: true
        output:
          type: object
          properties: {}
          additionalProperties: true
        status:
          type: string
          description: >-
            PENDING and RUNNING are status placeholders; all other listed values
            are terminal task outcomes.
          enum:
            - PENDING
            - RUNNING
            - SUCCESS
            - ERROR
            - TIMEOUT
            - BLOCKED
            - CAPTCHA_FAILED
            - LOOP
            - NO_TARGET
            - EXHAUSTED
        data:
          description: >-
            Extracted result; shape follows the requested or inferred schema.
            May be null or omitted.
        creditsUsed:
          type: number
          description: >-
            Settled task charge: every task keeps the 0.1-credit floor charged
            at submission; every status except ERROR and EXHAUSTED adds any
            usage above it.
        durationMs:
          type: integer
          description: Wall-clock task duration in milliseconds.
        error:
          anyOf:
            - type: string
            - type: 'null'
        attempts:
          type: integer
        queuePosition:
          anyOf:
            - type: integer
              description: Queue estimate; 0 when running.
            - type: 'null'
        events:
          type: array
          items:
            $ref: '#/components/schemas/RunnerEvent'
          description: Available timeline when includeEvents=true.
      additionalProperties: true
    RunnerEvent:
      type: object
      required:
        - type
        - timestamp
      properties:
        type:
          type: string
          description: >-
            Extensible event type, for example navigation, screenshot, plan,
            action, validation, waiting, done, or error.
        timestamp:
          type: number
          description: Unix timestamp in milliseconds.
        iteration:
          type: integer
        data:
          description: >-
            Event-specific payload. Screenshot data is not guaranteed to be a
            URL.
      additionalProperties: true
  securitySchemes:
    apiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: >-
        Your pre.dev workspace API key (pdk_...) from Integrations, Built-in
        tab: https://pre.dev/projects/integrations?tab=built-in
    xApiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: 'Alternative to Authorization: Bearer.'

````

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