{
  "openapi": "3.1.0",
  "info": {
    "title": "Parse API",
    "description": "Programmatic API for creating, managing, and executing web scraping APIs.",
    "version": "2.0.0"
  },
  "servers": [
    {
      "url": "https://api.parse.bot",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Execute",
      "description": "Call the endpoints of an API you've built — the most common operation"
    },
    {
      "name": "Marketplace",
      "description": "Browse and search the public marketplace of pre-built APIs (no auth required)"
    },
    {
      "name": "Dispatch",
      "description": "Create and manage scraping APIs"
    },
    {
      "name": "Export",
      "description": "Export API specs in standard formats"
    },
    {
      "name": "Updates",
      "description": "Check and merge upstream improvements to your APIs"
    }
  ],
  "paths": {
    "/dispatch": {
      "post": {
        "tags": [
          "Dispatch"
        ],
        "summary": "Create a new API from a URL",
        "description": "Submit a website URL to generate an API. If a matching API already exists, returns it instantly. Otherwise, queues a job to build one. Optionally describe the data you need in the `task` field.",
        "operationId": "create_dispatch_task",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DispatchRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task created or matched",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DispatchResponse"
                }
              }
            }
          }
        }
      }
    },
    "/dispatch/tasks": {
      "get": {
        "tags": [
          "Dispatch"
        ],
        "summary": "List your APIs",
        "description": "List all dispatch tasks for the current user, with optional status filter and pagination.",
        "operationId": "list_tasks",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by status (queued, running, completed, failed, needs_input)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Max results to return",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of results to skip",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of tasks",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskListResponse"
                }
              }
            }
          }
        }
      }
    },
    "/dispatch/tasks/{task_id}": {
      "get": {
        "tags": [
          "Dispatch"
        ],
        "summary": "Get task detail",
        "description": "Get full details for a dispatch task, including the generated API spec with endpoint definitions and execution URLs when completed.",
        "operationId": "get_task",
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Task detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskDetail"
                }
              }
            }
          }
        }
      }
    },
    "/dispatch/{task_id}": {
      "post": {
        "tags": [
          "Dispatch"
        ],
        "summary": "Respond to input prompts",
        "description": "Send a response when the agent requests user input during API creation. Task status must be 'needs_input'.",
        "operationId": "respond_to_input",
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserResponseRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/dispatch/tasks/{task_id}/revise": {
      "post": {
        "tags": [
          "Dispatch"
        ],
        "summary": "Revise a completed API",
        "description": "Submit a revision request for a completed API. Describe what to change — the system will classify and queue the revision.",
        "operationId": "revise_task",
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RevisionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Revision queued",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevisionResponse"
                }
              }
            }
          }
        }
      }
    },
    "/dispatch/tasks/{task_id}/cancel": {
      "post": {
        "tags": [
          "Dispatch"
        ],
        "summary": "Cancel a task",
        "description": "Cancel an in-flight build or revision task. Only tasks in a non-terminal state (queued, running, needs_input) can be cancelled.",
        "operationId": "cancel_task",
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cancellation result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "task_id": {
                      "type": "string"
                    },
                    "previous_status": {
                      "type": "string"
                    },
                    "cancelled": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Task not found"
          },
          "409": {
            "description": "Task is already in a terminal state"
          }
        }
      }
    },
    "/dispatch/scrapers/{scraper_id}/active-task": {
      "get": {
        "tags": [
          "Dispatch"
        ],
        "summary": "Get the in-flight task for a scraper",
        "description": "Return the active (queued/running/needs_input) revision or extension task for a given scraper, if any. Useful for showing live progress on an API you already own. Returns `{ \"task\": null }` when nothing is in flight.",
        "operationId": "get_active_task",
        "parameters": [
          {
            "name": "scraper_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Active task or null",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "task": {
                      "nullable": true,
                      "$ref": "#/components/schemas/TaskDetail"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/dispatch/tasks/{task_id}/sdk-example": {
      "get": {
        "tags": [
          "Dispatch"
        ],
        "summary": "Get SDK usage example",
        "description": "Get a code example showing how to call this API using the Parse SDK.",
        "operationId": "get_sdk_example",
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SDK example",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sdk_usage_example": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "code": {
                          "type": "string"
                        },
                        "endpoints": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "has_session": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/dispatch/tasks/{task_id}/revisions": {
      "get": {
        "tags": [
          "Dispatch"
        ],
        "summary": "List revision history",
        "description": "List all revisions (child tasks) for a given task.",
        "operationId": "list_revisions",
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of revision tasks",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskListResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/apis/{scraper_id}/openapi.json": {
      "get": {
        "tags": [
          "Export"
        ],
        "summary": "Get a callable API's OpenAPI document",
        "description": "Return the complete OpenAPI 3.1 document for an API you own or an active marketplace canonical. Optionally pin it to an available version snapshot.",
        "operationId": "get_openapi_document",
        "parameters": [
          {
            "name": "scraper_id",
            "in": "path",
            "required": true,
            "description": "The callable scraper ID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "version",
            "in": "query",
            "required": false,
            "description": "Pin the document to a version snapshot. Marketplace canonicals accept release versions only; omit for the live document.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OpenAPI 3.1 specification",
            "headers": {
              "ETag": {
                "description": "Strong validator for the returned document.",
                "schema": {
                  "type": "string"
                }
              },
              "API-Snapshot-Version": {
                "description": "The pinned version, when version was requested.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "304": {
            "description": "The document matches If-None-Match."
          },
          "404": {
            "description": "The API or requested version is unavailable to the caller."
          }
        }
      }
    },
    "/dispatch/tasks/{task_id}/export/mcp": {
      "get": {
        "tags": [
          "Export"
        ],
        "summary": "Export as MCP tools",
        "description": "Export a completed API as MCP (Model Context Protocol) tool definitions. Returns tool names, input schemas, and endpoint metadata.",
        "operationId": "export_mcp",
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "MCP tool definitions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tools": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "inputSchema": {
                            "type": "object"
                          },
                          "annotations": {
                            "type": "object"
                          }
                        }
                      }
                    },
                    "server_info": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "description": {
                          "type": "string"
                        },
                        "base_url": {
                          "type": "string"
                        },
                        "auth_header": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/dispatch/tasks/{task_id}/updates": {
      "get": {
        "tags": [
          "Dispatch"
        ],
        "summary": "Check for available updates",
        "description": "Compare your API against the canonical version to find new or updated endpoints. Returns a list of changes you can preview and merge.",
        "operationId": "check_updates",
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Available updates",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updates": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EndpointUpdate"
                      }
                    },
                    "canonical_scraper_id": {
                      "type": "string",
                      "nullable": true,
                      "description": "ID of the canonical scraper (present when updates exist)"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/dispatch/tasks/{task_id}/merge-updates": {
      "post": {
        "tags": [
          "Dispatch"
        ],
        "summary": "Merge updates from canonical",
        "description": "Apply selected endpoint updates from the canonical version into your API. New endpoints also merge code; spec-only updates preserve your code.",
        "operationId": "merge_updates",
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MergeUpdatesRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Merge result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MergeUpdatesResponse"
                }
              }
            }
          }
        }
      }
    },
    "/dispatch/tasks/{task_id}/rollback-merge": {
      "post": {
        "tags": [
          "Dispatch"
        ],
        "summary": "Rollback last merge",
        "description": "Restore your API to its pre-merge state. Only the most recent merge can be rolled back.",
        "operationId": "rollback_merge",
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rollback result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rolled_back": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "No merge to rollback"
          }
        }
      }
    },
    "/dispatch/tasks/{task_id}/preview-update/{endpoint_name}": {
      "post": {
        "tags": [
          "Dispatch"
        ],
        "summary": "Preview an endpoint update",
        "description": "Test an endpoint against the canonical version to see the updated behavior before merging. Returns the execution result plus sample inputs.",
        "operationId": "preview_update",
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "endpoint_name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "params": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Test parameters for the endpoint"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Execution result from the canonical version",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object",
                      "nullable": true
                    },
                    "sample_inputs": {
                      "type": "object",
                      "description": "Pre-fill data from canonical for this endpoint"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/marketplace/apis": {
      "get": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Search and browse the marketplace",
        "description": "Search the public marketplace of pre-built APIs. No authentication required. Returns hybrid-ranked results when `q` is provided, or a browseable list ordered by `sort` otherwise. Use this to find an existing API for a site before building your own with POST /dispatch.",
        "operationId": "search_marketplace",
        "security": [],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Free-text search across API name, description, and endpoints.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Filter by category slug.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Ordering when not searching: 'top' (highest rated), 'recent' (newest), or 'name' (A→Z).",
            "schema": {
              "type": "string",
              "enum": [
                "top",
                "recent",
                "name"
              ],
              "default": "top"
            }
          },
          {
            "name": "country",
            "in": "query",
            "required": false,
            "description": "ISO-2 country code for region-aware ranking (e.g. 'US').",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "semantic",
            "in": "query",
            "required": false,
            "description": "Include the semantic (embedding) ranking arm when searching.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 200
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Marketplace search results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketplaceListResponse"
                }
              }
            }
          }
        }
      }
    },
    "/marketplace/search-endpoints": {
      "get": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Search marketplace endpoints",
        "description": "Search across individual endpoints in the marketplace (not just APIs). No authentication required. Returns two groups: matching APIs and matching endpoints. Useful when you know the data you want but not which API provides it.",
        "operationId": "search_marketplace_endpoints",
        "security": [],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Free-text query (1–120 chars).",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 120
            }
          },
          {
            "name": "api_limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 6,
              "minimum": 1,
              "maximum": 20
            }
          },
          {
            "name": "endpoint_limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 12,
              "minimum": 1,
              "maximum": 40
            }
          },
          {
            "name": "country",
            "in": "query",
            "required": false,
            "description": "ISO-2 country code for region-aware ranking.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Combined API and endpoint search results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/marketplace/apis/{id}": {
      "get": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Get a marketplace API's detail",
        "description": "Full detail for one marketplace listing, including its `canonical_scraper_id` and endpoint list. No authentication required. Use `canonical_scraper_id` to call the shared canonical directly: POST /scraper/{canonical_scraper_id}/{endpoint_name}.",
        "operationId": "get_marketplace_api",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Marketplace API detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketplaceDetail"
                }
              }
            }
          },
          "404": {
            "description": "Listing not found"
          }
        }
      }
    },
    "/marketplace/apis/{id}/subscribe": {
      "post": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Subscribe to a marketplace API",
        "description": "Create your own copy of the canonical, pinned to its current release and added to your account (My APIs). Returns a `scraper_id` you call at POST /scraper/{scraper_id}/{endpoint_name}. Pinned, so upstream changes don't alter your contract until you merge updates. Stays sync-eligible.",
        "operationId": "subscribe_to_marketplace_api",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Subscribed — your pinned copy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscribeResponse"
                }
              }
            }
          },
          "404": {
            "description": "Listing not found"
          }
        }
      }
    },
    "/marketplace/apis/{id}/fork": {
      "post": {
        "tags": [
          "Marketplace"
        ],
        "summary": "Fork a marketplace API privately",
        "description": "Create a private, desynced copy of the canonical: no upstream merges, and your revisions don't propagate back. Returns a `scraper_id` you own and call at POST /scraper/{scraper_id}/{endpoint_name}.",
        "operationId": "fork_marketplace_api",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Forked — your private copy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForkResponse"
                }
              }
            }
          },
          "404": {
            "description": "Listing not found"
          }
        }
      }
    },
    "/scraper/{scraper_id}/{endpoint_name}": {
      "get": {
        "tags": [
          "Execute"
        ],
        "summary": "Execute an API endpoint (GET)",
        "description": "Call a generated API endpoint using query parameters. Use GET when the endpoint spec defines GET as the method. Parameters are passed as query strings and auto-coerced to the correct types.",
        "operationId": "run_scraper_endpoint_get",
        "parameters": [
          {
            "name": "scraper_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "endpoint_name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success — the endpoint's own JSON (shape defined by its return_schema).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScraperRunResponse"
                },
                "example": {
                  "title": "A Light in the Attic",
                  "price": 51.77,
                  "rating": 3
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/StaleInput"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ScraperError"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          },
          "503": {
            "$ref": "#/components/responses/Blocked"
          }
        }
      },
      "post": {
        "tags": [
          "Execute"
        ],
        "summary": "Execute an API endpoint (POST)",
        "description": "Call a generated API endpoint by scraper ID and endpoint name. Pass endpoint parameters in the JSON body.",
        "operationId": "run_scraper_endpoint",
        "parameters": [
          {
            "name": "scraper_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "endpoint_name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true,
                "description": "Endpoint-specific parameters (see the endpoint's input_params for the schema)"
              },
              "example": {
                "page": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success — the endpoint's own JSON (shape defined by its return_schema).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScraperRunResponse"
                },
                "example": {
                  "title": "A Light in the Attic",
                  "price": 51.77,
                  "rating": 3
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/StaleInput"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ScraperError"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          },
          "503": {
            "$ref": "#/components/responses/Blocked"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "API key for programmatic access. Create one in the Parse dashboard at https://parse.bot (Settings → API Keys). Keys start with 'pmx_'."
      }
    },
    "responses": {
      "StaleInput": {
        "description": "422 — your input was invalid or the resource is gone upstream (`status: stale_input`). Fix the input and retry.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ExecutionError"
            },
            "example": {
              "error": {
                "status": "stale_input",
                "kind": "input_format_invalid",
                "message": "trip_type must be 'one_way' or 'round_trip'"
              },
              "status_code": 422
            }
          }
        }
      },
      "RateLimited": {
        "description": "429 — rate limit exceeded. Honor the Retry-After header.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ExecutionError"
            },
            "example": {
              "error": {
                "error": "Rate limit exceeded",
                "message": "Too many requests in a short burst. Retry in 5s.",
                "limit_type": "burst",
                "retry_after": 5
              },
              "status_code": 429
            }
          }
        }
      },
      "ScraperError": {
        "description": "500 — the scraper crashed or hit a bug (`status: error`). Not your fault; revise the API.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ExecutionError"
            },
            "example": {
              "error": {
                "status": "error",
                "kind": "scraper_bug",
                "message": "The scraper crashed during execution. This could be due to bad input, broken scraper code, or a website update."
              },
              "status_code": 500
            }
          }
        }
      },
      "UpstreamError": {
        "description": "502 — the TARGET SITE returned a non-2xx (`status: upstream_error`). This is NOT a Parse outage; inspect upstream_status_code + snippet before retrying.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ExecutionError"
            },
            "example": {
              "error": {
                "status": "upstream_error",
                "upstream_status_code": 404,
                "message": "The target site returned HTTP 404. See `snippet` for the upstream response body.",
                "snippet": "<html>Product not found</html>",
                "url": "https://example.com/products/invalid-sku"
              },
              "status_code": 502
            }
          }
        }
      },
      "Blocked": {
        "description": "503 — anti-bot blocked every proxy (`status: blocked`). Retry later only if retry_after is present.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ExecutionError"
            },
            "example": {
              "error": {
                "status": "blocked",
                "block_type": "datadome",
                "vendor": "datadome",
                "kind": "antibot_solvable",
                "attempts": 3,
                "retry_after": 60,
                "message": "Service temporarily unavailable - site protection blocking all proxies"
              },
              "status_code": 503
            }
          }
        }
      }
    },
    "schemas": {
      "DispatchRequest": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "description": "Website URL to generate an API from"
          },
          "task": {
            "type": "string",
            "nullable": true,
            "description": "Natural language description of the data you need (e.g. 'get product prices and reviews')"
          },
          "contributes_to_marketplace": {
            "type": "boolean",
            "default": true,
            "description": "Whether this API contributes to the shared marketplace canonical for its domain. TRUE (default) = your revisions feed back into the canonical, you can merge upstream improvements, and the build is free. FALSE = the API is fully desynced from the canonical and the build is charged. This is a permanent, per-API choice and cannot be toggled later. (The old `force_new` field has been removed — sending it now returns a 400.)"
          },
          "allow_auth": {
            "type": "boolean",
            "default": false,
            "description": "Permit this build to proceed when the site requires a login. Programmatic (API key / MCP) callers must opt in: an authenticated build pauses at status=needs_input waiting for credentials, consumes a per-domain auth session, and runs as a private build. Requires a paid plan and a one-time disclaimer acceptance in the Parse dashboard. Setting this does NOT assert the site needs auth — that verdict stays server-side."
          },
          "workspace": {
            "type": "boolean",
            "default": false,
            "description": "Build this API directly into the caller's team workspace instead of their personal space, so every teammate can see and revise it. Requires a workspace on a Team plan; otherwise the dispatch is rejected with 402 rather than silently landing as personal."
          }
        }
      },
      "DispatchResponse": {
        "type": "object",
        "required": [
          "task_id",
          "matched"
        ],
        "properties": {
          "task_id": {
            "type": "string",
            "description": "Unique task identifier — use this to poll for status"
          },
          "matched": {
            "type": "boolean",
            "description": "True if an existing API was found for this URL"
          },
          "test_inputs": {
            "type": "array",
            "nullable": true,
            "description": "Suggested test parameters for each endpoint (when matched)"
          },
          "may_require_auth": {
            "type": "boolean",
            "default": false,
            "description": "True if the task likely requires authentication credentials"
          }
        }
      },
      "TaskDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "task": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "needs_input",
              "completed",
              "failed",
              "cancelled"
            ],
            "description": "Task lifecycle state. Poll until it reaches a terminal state: completed, failed, or cancelled."
          },
          "iteration": {
            "type": "integer",
            "nullable": true
          },
          "user_input_prompt": {
            "type": "object",
            "nullable": true,
            "description": "Present when status is 'needs_input' — the question(s) the agent needs answered. Respond via POST /dispatch/{task_id}."
          },
          "result_scraper_id": {
            "type": "string",
            "nullable": true,
            "description": "Set on completion — the scraper_id used to execute endpoints."
          },
          "result_marketplace_id": {
            "type": "string",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true,
            "description": "Set when status is 'failed'."
          },
          "created_at": {
            "type": "string",
            "nullable": true
          },
          "updated_at": {
            "type": "string",
            "nullable": true
          },
          "completed_at": {
            "type": "string",
            "nullable": true
          },
          "parent_task_id": {
            "type": "string",
            "nullable": true
          },
          "extends_scraper_id": {
            "type": "string",
            "nullable": true
          },
          "revision_type": {
            "type": "string",
            "nullable": true
          },
          "auth_enabled": {
            "type": "boolean",
            "default": false,
            "description": "True if this API was built against a site that requires login."
          },
          "source": {
            "type": "string",
            "enum": [
              "built",
              "subscribed",
              "forked"
            ],
            "default": "built",
            "description": "How the user acquired this API."
          },
          "contributes_to_marketplace": {
            "type": "boolean",
            "default": true
          },
          "progress": {
            "type": "object",
            "description": "Live build progress while status is 'running' (stage, headline, per-endpoint status, log).",
            "additionalProperties": true
          },
          "marketplace_apis": {
            "type": "object",
            "nullable": true
          },
          "existing_marketplace_apis": {
            "type": "object",
            "nullable": true
          },
          "generated_api": {
            "nullable": true,
            "$ref": "#/components/schemas/GeneratedAPI"
          }
        }
      },
      "TaskListResponse": {
        "type": "object",
        "properties": {
          "tasks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaskDetail"
            }
          },
          "total": {
            "type": "integer"
          }
        }
      },
      "GeneratedAPI": {
        "type": "object",
        "properties": {
          "marketplace_id": {
            "type": "string"
          },
          "scraper_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "source_url": {
            "type": "string"
          },
          "execution_base_url": {
            "type": "string",
            "description": "Base URL for executing endpoints: {execution_base_url}/{endpoint_name}"
          },
          "endpoints": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EndpointSpec"
            }
          }
        }
      },
      "EndpointSpec": {
        "type": "object",
        "properties": {
          "method": {
            "type": "string",
            "description": "HTTP method (GET, POST, etc.)"
          },
          "endpoint_name": {
            "type": "string",
            "description": "Endpoint path segment"
          },
          "description": {
            "type": "string"
          },
          "input_params": {
            "type": "object"
          },
          "return_schema": {
            "type": "object"
          },
          "initiates_session": {
            "type": "boolean",
            "description": "True for a login endpoint that returns a session — see Authenticated APIs."
          }
        }
      },
      "UserResponseRequest": {
        "type": "object",
        "required": [
          "user_response"
        ],
        "properties": {
          "user_response": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Key-value pairs responding to the agent's input prompt"
          }
        }
      },
      "RevisionRequest": {
        "type": "object",
        "required": [
          "revision"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "description": "Natural language description of what to change (e.g. 'add a search by category endpoint')"
          },
          "contributes_to_marketplace": {
            "type": "boolean",
            "default": true,
            "description": "Whether this revision contributes back to the shared marketplace canonical. TRUE (default) = the revision propagates upstream and is free. FALSE = the revision desyncs this API from the canonical and is charged. The opt-out is permanent."
          }
        }
      },
      "RevisionResponse": {
        "type": "object",
        "properties": {
          "task_id": {
            "type": "string",
            "description": "ID of the revision task (may be the original if no agent run was needed)"
          },
          "revision_type": {
            "type": "string",
            "description": "Always 'revision' — a single agent handles all revision types"
          }
        }
      },
      "ScraperRunResponse": {
        "type": "object",
        "description": "On success (HTTP 200) the body is the endpoint's own JSON — the shape described by its return_schema — not a fixed wrapper. This generic schema documents the fallback fields present when output can't be parsed. See the ExecutionError schema for non-2xx responses.",
        "additionalProperties": true,
        "properties": {
          "status": {
            "type": "string",
            "description": "Present on fallback/error envelopes; absent on a normal successful response (which is just the endpoint's data)."
          },
          "scraper_id": {
            "type": "string"
          },
          "execution_time": {
            "type": "number",
            "nullable": true,
            "description": "Execution time in seconds"
          }
        }
      },
      "ExecutionError": {
        "type": "object",
        "description": "Non-2xx execution envelope. `error` is either a string or an object whose `status` field (success/stale_input/upstream_error/blocked/error) tells you the failure category — see the Errors guide.",
        "properties": {
          "error": {
            "description": "A message string, or a detail object with a machine-readable `status` and context."
          },
          "status_code": {
            "type": "integer"
          }
        }
      },
      "EndpointUpdate": {
        "type": "object",
        "properties": {
          "endpoint_name": {
            "type": "string",
            "description": "Name of the endpoint with an available update"
          },
          "type": {
            "type": "string",
            "enum": [
              "new",
              "updated"
            ],
            "description": "'new' if the endpoint didn't exist when you cloned, 'updated' if the canonical version changed"
          },
          "description": {
            "type": "string"
          },
          "method": {
            "type": "string",
            "description": "HTTP method (GET, POST, etc.)"
          },
          "user_has_called": {
            "type": "boolean",
            "description": "Whether you've previously called this endpoint"
          }
        }
      },
      "MergeUpdatesRequest": {
        "type": "object",
        "required": [
          "endpoints"
        ],
        "properties": {
          "endpoints": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Endpoint names to merge from canonical"
          }
        }
      },
      "MergeUpdatesResponse": {
        "type": "object",
        "properties": {
          "merged": {
            "type": "integer",
            "description": "Number of endpoints merged"
          },
          "endpoints": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Endpoint names that were merged"
          },
          "code_merged": {
            "type": "boolean",
            "description": "Whether scraper code was also updated (true when new endpoints are merged)"
          }
        }
      },
      "MarketplaceListResponse": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MarketplaceListing"
            }
          },
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          }
        }
      },
      "MarketplaceListing": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Marketplace listing id. Use it with GET /marketplace/apis/{id} (detail + canonical_scraper_id), or /subscribe and /fork."
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "front_facing_description": {
            "type": "string",
            "nullable": true
          },
          "source_url": {
            "type": "string",
            "nullable": true
          },
          "is_authenticated": {
            "type": "boolean",
            "nullable": true,
            "description": "True if the API requires login."
          },
          "endpoint_count": {
            "type": "integer"
          },
          "updated_at": {
            "type": "string",
            "nullable": true
          },
          "primary_category": {
            "type": "string",
            "nullable": true
          },
          "secondary_categories": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "endpoint_preview": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "endpoint_name": {
                  "type": "string"
                },
                "method": {
                  "type": "string"
                },
                "summary": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          }
        }
      },
      "MarketplaceDetail": {
        "type": "object",
        "description": "Full listing detail. additionalProperties is allowed — only the fields most useful for calling the API are documented here.",
        "additionalProperties": true,
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "source_url": {
            "type": "string",
            "nullable": true
          },
          "canonical_scraper_id": {
            "type": "string",
            "nullable": true,
            "description": "The shared canonical scraper id. Call it directly with any API key: POST /scraper/{canonical_scraper_id}/{endpoint_name}."
          },
          "is_authenticated": {
            "type": "boolean",
            "nullable": true
          },
          "apis": {
            "type": "array",
            "description": "The API's endpoints (method, name, input_params, return_schema, initiates_session).",
            "items": {
              "$ref": "#/components/schemas/EndpointSpec"
            }
          },
          "latest_version": {
            "type": "integer",
            "nullable": true,
            "description": "Canonical's latest release version."
          },
          "pinned_version": {
            "type": "integer",
            "nullable": true,
            "description": "The version your subscription is pinned to, if you've subscribed."
          },
          "user_status": {
            "type": "string",
            "nullable": true,
            "enum": [
              "none",
              "subscribed",
              "forked"
            ],
            "description": "Your relationship to this API (when authenticated)."
          },
          "user_scraper_id": {
            "type": "string",
            "nullable": true,
            "description": "If you've subscribed or forked, your own scraper_id to call instead of the canonical."
          },
          "primary_category": {
            "type": "string",
            "nullable": true
          },
          "secondary_categories": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "SubscribeResponse": {
        "type": "object",
        "properties": {
          "scraper_id": {
            "type": "string",
            "description": "Your pinned copy — call POST /scraper/{scraper_id}/{endpoint_name}."
          },
          "canonical_scraper_id": {
            "type": "string",
            "description": "The shared canonical id (the 'global' URL you can also call directly)."
          }
        }
      },
      "ForkResponse": {
        "type": "object",
        "properties": {
          "scraper_id": {
            "type": "string",
            "description": "Your private, desynced copy — call POST /scraper/{scraper_id}/{endpoint_name}."
          },
          "canonical_scraper_id": {
            "type": "string"
          }
        }
      }
    }
  }
}