openapi: 3.0.3
info:
  title: QuanticData API
  version: "1.0"
  description: |
    REST API for scrape, SERP, map, crawl, batch, SEO audit, collectors and
    proxy generation (list plans, generate endpoint strings, whitelist IPs, geo).
    One host, one Bearer key, one JSON envelope. Failed Data API calls are never billed.
    $2 free every month, no card. PAYG is 60 requests per minute.

    Canonical HTML reference: https://quanticdata.io/docs/
    Markdown twin: https://quanticdata.io/docs/index.md
  contact:
    name: QuanticData
    email: hello@quanticdata.io
    url: https://quanticdata.io/
  termsOfService: https://quanticdata.io/terms/
externalDocs:
  description: API reference
  url: https://quanticdata.io/docs/
servers:
  - url: https://api.quanticdata.io
    description: Primary
  - url: https://app.quanticdata.io/api
    description: App-host fallback (same API)
tags:
  - name: Scrape
  - name: SERP
  - name: Map
  - name: Crawl
  - name: Batch
  - name: SEO audit
  - name: Collectors
  - name: Proxies
security:
  - bearerAuth: []
  - apiKey: []
paths:
  /v1/scrape:
    post:
      tags: [Scrape]
      operationId: scrape
      summary: Scrape one page
      description: |
        One URL through the residential pool, returned as Markdown, HTML or text.
        Starts on a TLS-fingerprint tier and escalates to a stealth browser only on a block,
        or when you set render true. Alias path: POST /v1/scraper/extract.
        List price $0.0002 per page, $0.001 when rendered.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ScrapeRequest"
            example:
              url: https://example.com
              format: markdown
      responses:
        "200":
          description: Extraction successful (or type error in the envelope)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ScrapeEnvelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/Error"
  /v1/scraper/extract:
    post:
      tags: [Scrape]
      operationId: scrapeAlias
      summary: Scrape one page (alias)
      description: Same as POST /v1/scrape.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ScrapeRequest"
      responses:
        "200":
          description: Extraction successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ScrapeEnvelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
  /v1/serp:
    post:
      tags: [SERP]
      operationId: serp
      summary: Search engine results
      description: |
        Google, Bing or DuckDuckGo as JSON. 17 verticals on Google.
        Alias POST /v1/scraper/serp. List price from $0.0005 ($0.002 rendered).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SerpRequest"
            example:
              query: best coffee grinder
              engine: google
              country: us
      responses:
        "200":
          description: SERP successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SerpEnvelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
  /v1/scraper/serp:
    post:
      tags: [SERP]
      operationId: serpAlias
      summary: Search engine results (alias)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SerpRequest"
      responses:
        "200":
          description: SERP successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SerpEnvelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
  /v1/map:
    post:
      tags: [Map]
      operationId: mapSite
      summary: List URLs on a site
      description: Every URL of a site (sitemaps + homepage links), no page bodies. $0.0005 per site. Alias POST /v1/scraper/map.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MapRequest"
            example:
              url: https://example.com
              limit: 100
      responses:
        "200":
          description: Map successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MapEnvelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
  /v1/scraper/map:
    post:
      tags: [Map]
      operationId: mapSiteAlias
      summary: List URLs on a site (alias)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MapRequest"
      responses:
        "200":
          description: Map successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MapEnvelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
  /v1/crawl:
    post:
      tags: [Crawl]
      operationId: crawlStart
      summary: Start an async crawl
      description: BFS from a seed URL (cap 500 pages, depth 10). Charged up front; unfetched pages refunded. $0.0003 per page. Alias POST /v1/scraper/crawl.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CrawlRequest"
            example:
              url: https://example.com
              limit: 20
              depth: 2
      responses:
        "200":
          description: Crawl started
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CrawlStartEnvelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
  /v1/crawl/{jobId}:
    get:
      tags: [Crawl]
      operationId: crawlStatus
      summary: Poll a crawl job
      parameters:
        - $ref: "#/components/parameters/jobId"
        - $ref: "#/components/parameters/includeContent"
        - $ref: "#/components/parameters/since"
      responses:
        "200":
          description: Job snapshot
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CrawlPollEnvelope"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
    delete:
      tags: [Crawl]
      operationId: crawlCancel
      summary: Cancel a running crawl
      parameters:
        - $ref: "#/components/parameters/jobId"
      responses:
        "200":
          description: Cancelled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
  /v1/scraper/crawl:
    post:
      tags: [Crawl]
      operationId: crawlStartAlias
      summary: Start an async crawl (alias)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CrawlRequest"
      responses:
        "200":
          description: Crawl started
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CrawlStartEnvelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
  /v1/scraper/crawl/{jobId}:
    get:
      tags: [Crawl]
      operationId: crawlStatusAlias
      summary: Poll a crawl job (alias)
      parameters:
        - $ref: "#/components/parameters/jobId"
        - $ref: "#/components/parameters/includeContent"
        - $ref: "#/components/parameters/since"
      responses:
        "200":
          description: Job snapshot
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CrawlPollEnvelope"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
    delete:
      tags: [Crawl]
      operationId: crawlCancelAlias
      summary: Cancel a running crawl (alias)
      parameters:
        - $ref: "#/components/parameters/jobId"
      responses:
        "200":
          description: Cancelled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "401":
          $ref: "#/components/responses/Error"
  /v1/batch:
    post:
      tags: [Batch]
      operationId: batchStart
      summary: Scrape a list of known URLs
      description: Up to 5,000 known URLs, async. Charged up front; failed share refunded. $0.0002 per URL. Alias POST /v1/scraper/batch.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchRequest"
            example:
              urls:
                - https://example.com
                - https://example.org
              format: markdown
      responses:
        "200":
          description: Batch started
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
  /v1/batch/{jobId}:
    get:
      tags: [Batch]
      operationId: batchStatus
      summary: Poll a batch job
      parameters:
        - $ref: "#/components/parameters/jobId"
        - $ref: "#/components/parameters/includeContent"
      responses:
        "200":
          description: Job snapshot
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
  /v1/scraper/batch:
    post:
      tags: [Batch]
      operationId: batchStartAlias
      summary: Scrape a list of known URLs (alias)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchRequest"
      responses:
        "200":
          description: Batch started
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
  /v1/scraper/batch/{jobId}:
    get:
      tags: [Batch]
      operationId: batchStatusAlias
      summary: Poll a batch job (alias)
      parameters:
        - $ref: "#/components/parameters/jobId"
        - $ref: "#/components/parameters/includeContent"
      responses:
        "200":
          description: Job snapshot
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
  /v1/seo-audit:
    post:
      tags: [SEO audit]
      operationId: seoAudit
      summary: No-JS vs rendered audit
      description: Fetches twice (pure HTTP and fully rendered) and returns both views plus the diff. $0.0012 per URL. Alias POST /v1/scraper/seo-audit.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SeoAuditRequest"
            example:
              url: https://example.com
      responses:
        "200":
          description: SEO audit successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SeoAuditEnvelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
  /v1/scraper/seo-audit:
    post:
      tags: [SEO audit]
      operationId: seoAuditAlias
      summary: No-JS vs rendered audit (alias)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SeoAuditRequest"
      responses:
        "200":
          description: SEO audit successful
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SeoAuditEnvelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
  /v1/scraper/collectors:
    get:
      tags: [Collectors]
      operationId: listCollectors
      summary: Catalog of ready-made scrapers
      description: 74 collectors with input_schema, examples, health and unit price. Catalog call is free.
      responses:
        "200":
          description: Catalog
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "401":
          $ref: "#/components/responses/Error"
  /v1/scraper/collectors/{slug}:
    get:
      tags: [Collectors]
      operationId: getCollector
      summary: One collector schema
      parameters:
        - $ref: "#/components/parameters/slug"
      responses:
        "200":
          description: Collector
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
  /v1/scraper/collectors/{slug}/run:
    post:
      tags: [Collectors]
      operationId: runCollector
      summary: Run a collector
      description: Body is the collector's semantic input (never a URL list). Billed per delivered result, from $0.0005.
      parameters:
        - $ref: "#/components/parameters/slug"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CollectorRunRequest"
            example:
              query: pizza restaurants
              location: Brooklyn, NY
              country: us
              max_results: 20
      responses:
        "200":
          description: Run complete
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectorRunEnvelope"
        "202":
          description: Run accepted (async)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/RateLimited"
  /v1/scraper/collectors/runs:
    get:
      tags: [Collectors]
      operationId: listCollectorRuns
      summary: Your collector runs
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
      responses:
        "200":
          description: Run list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "401":
          $ref: "#/components/responses/Error"
  /v1/scraper/collectors/runs/{runId}:
    get:
      tags: [Collectors]
      operationId: getCollectorRun
      summary: One collector run
      parameters:
        - name: runId
          in: path
          required: true
          schema:
            type: string
        - name: format
          in: query
          schema:
            type: string
            enum: [json, csv]
      responses:
        "200":
          description: Run payload or CSV
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectorRunEnvelope"
            text/csv:
              schema:
                type: string
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
  /v1/public/proxies:
    get:
      tags: [Proxies]
      operationId: listProxies
      summary: List proxy plans
      description: |
        List the account's proxy services (Residential Basic/Premium/Private, Mobile, Mobile V2, Datacenter, ISP, IPv6) with orderId, bandwidth left, expiry and whitelist. Use orderId on generate.
      parameters:
        - $ref: "#/components/parameters/planTypeQuery"
        - name: active
          in: query
          schema:
            type: boolean
          description: true = non-expired only; false = expired only; omit for all.
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
            minimum: 1
            maximum: 100
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
            minimum: 0
      responses:
        "200":
          description: Plan list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "401":
          $ref: "#/components/responses/Error"
  /v1/public/proxies/generate:
    post:
      tags: [Proxies]
      operationId: generateProxies
      summary: Generate proxy endpoint strings
      description: |
        Ready-to-use proxy strings (credentials included) from an active plan. Same key as the Data API. Requires an orderId from GET /v1/public/proxies.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GenerateProxiesRequest"
            example:
              orderId: your_order_id
              protocol: http
              format: "user:pass@host:port"
              quantity: 10
              country: us
              rotation: rotating
      responses:
        "200":
          description: Proxy strings
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenerateProxiesEnvelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
  /v1/public/proxies/whitelist-ip:
    get:
      tags: [Proxies]
      operationId: listWhitelistIp
      summary: List whitelisted IPs
      description: IP-auth whitelist for Residential Basic, Datacenter, ISP, IPv6, Mobile. Residential Premium/Private use user:pass and do not need this.
      parameters:
        - name: orderId
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Whitelist
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
    post:
      tags: [Proxies]
      operationId: addWhitelistIp
      summary: Whitelist an IP
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WhitelistIpRequest"
            example:
              orderId: your_order_id
              ip: "203.0.113.7"
      responses:
        "200":
          description: Whitelisted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
    delete:
      tags: [Proxies]
      operationId: removeWhitelistIp
      summary: Remove a whitelisted IP
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [orderId, ip]
              properties:
                orderId:
                  type: string
                ip:
                  type: string
                id:
                  type: string
                  description: Mobile also accepts an entry id.
      responses:
        "200":
          description: Removed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
  /v1/public/proxies/ip-info:
    get:
      tags: [Proxies]
      operationId: proxyIpInfo
      summary: Look up an exit IP
      description: Sticky sessions only. Rotating mode has no stable IP to look up.
      parameters:
        - name: ip
          in: query
          required: true
          schema:
            type: string
          description: IP to look up (?ip=x.x.x.x).
      responses:
        "200":
          description: Geo of the IP
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
  /v1/public/geo/countries:
    get:
      tags: [Proxies]
      operationId: proxyGeoCountries
      summary: Countries for a plan type
      parameters:
        - $ref: "#/components/parameters/planTypeRequired"
      responses:
        "200":
          description: Country list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
  /v1/public/geo/states:
    get:
      tags: [Proxies]
      operationId: proxyGeoStates
      summary: States/regions for a country
      parameters:
        - $ref: "#/components/parameters/planTypeRequired"
        - name: country
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: State list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
  /v1/public/geo/cities:
    get:
      tags: [Proxies]
      operationId: proxyGeoCities
      summary: Cities for a country
      parameters:
        - $ref: "#/components/parameters/planTypeRequired"
        - name: country
          in: query
          required: true
          schema:
            type: string
        - name: state
          in: query
          schema:
            type: string
      responses:
        "200":
          description: City list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
  /v1/public/geo/asns:
    get:
      tags: [Proxies]
      operationId: proxyGeoAsns
      summary: ASNs for targeting
      description: Residential Basic and Datacenter Basic.
      parameters:
        - $ref: "#/components/parameters/planTypeRequired"
        - name: country
          in: query
          schema:
            type: string
      responses:
        "200":
          description: ASN list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
  /v1/public/geo/locations:
    get:
      tags: [Proxies]
      operationId: proxyGeoLocations
      summary: Full location tree (Residential Premium)
      parameters:
        - name: planType
          in: query
          schema:
            type: string
            enum: [residentialpremium]
          description: Only residentialpremium is supported.
      responses:
        "200":
          description: Location tree
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
  /v1/public/generator/residential-premium/targeting-options:
    get:
      tags: [Proxies]
      operationId: targetingResidentialPremium
      summary: Residential Premium targeting tree
      responses:
        "200":
          description: Country → region → city → ISP
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "401":
          $ref: "#/components/responses/Error"
  /v1/public/generator/mobile/targeting-options:
    get:
      tags: [Proxies]
      operationId: targetingMobile
      summary: Mobile targeting tree
      responses:
        "200":
          description: Country → region → city → ISP
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "401":
          $ref: "#/components/responses/Error"
  /v1/public/generator/datacenter/targeting-options:
    get:
      tags: [Proxies]
      operationId: targetingDatacenter
      summary: Datacenter gateway list
      responses:
        "200":
          description: Static datacenter gateways
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Envelope"
        "401":
          $ref: "#/components/responses/Error"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: qd_live_
      description: Authorization Bearer qd_live_… One key covers Data APIs and proxy generation.
    apiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: Fallback if you cannot set Authorization. Do not put the key in the query string.
  parameters:
    jobId:
      name: jobId
      in: path
      required: true
      schema:
        type: string
    slug:
      name: slug
      in: path
      required: true
      schema:
        type: string
      example: google_maps_places
    includeContent:
      name: include_content
      in: query
      schema:
        type: boolean
      description: false for a light status check; true for page bodies.
    since:
      name: since
      in: query
      schema:
        type: string
      description: Cursor / timestamp for incremental crawl polls.
    planTypeQuery:
      name: planType
      in: query
      schema:
        $ref: "#/components/schemas/PlanType"
      description: Only services of this plan type.
    planTypeRequired:
      name: planType
      in: query
      required: true
      schema:
        $ref: "#/components/schemas/PlanType"
  responses:
    Error:
      description: Envelope type error. Never billed.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    RateLimited:
      description: Rate limit. Back off on Retry-After.
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds to wait
        X-RateLimit-Limit:
          schema:
            type: integer
        X-RateLimit-Remaining:
          schema:
            type: integer
        X-RateLimit-Reset:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
  schemas:
    Envelope:
      type: object
      required: [type, message]
      properties:
        type:
          type: string
          enum: [response, error]
        message:
          type: string
        payload:
          type: object
        pagination:
          type: object
    ErrorEnvelope:
      type: object
      required: [type, message]
      properties:
        type:
          type: string
          enum: [error]
        message:
          type: string
    Usage:
      type: object
      properties:
        cost_usd:
          type: number
        free_usd:
          type: number
        paid_usd:
          type: number
    ScrapeRequest:
      type: object
      properties:
        url:
          type: string
          maxLength: 2048
          description: Page to fetch (http/https). Optional when you pass html.
        html:
          type: string
          description: Convert markup you already have (max 5MB). No fetch, no proxy bandwidth.
        format:
          type: string
          enum: [markdown, html, text, raw]
          default: markdown
        formats:
          type: array
          items:
            type: string
        render:
          type: boolean
          default: false
        engine:
          type: string
          enum: [auto, tls, fetch, render]
          default: auto
        mobile:
          type: boolean
          default: false
        screenshot:
          description: true = viewport PNG, fullPage = whole page. Render mode.
          oneOf:
            - type: boolean
            - type: string
              enum: [fullPage]
        country:
          type: string
          description: ISO country code for the proxy exit.
        state:
          type: string
        city:
          type: string
        rotation:
          type: string
          enum: [rotating, sticky]
          default: rotating
        sessionId:
          type: string
          maxLength: 64
        sessionDuration:
          type: number
          default: 10
          minimum: 3
          maximum: 1440
        contentMode:
          type: string
          enum: [smart, article, full]
          default: smart
        content_mode:
          type: string
          description: Alias of contentMode.
        include_links:
          type: boolean
          default: false
        includeLinks:
          type: boolean
        waitMs:
          type: number
          maximum: 15000
        waitForSelector:
          type: string
        scrollToBottom:
          type: boolean
          default: false
        actions:
          type: array
          maxItems: 20
          items:
            type: object
            additionalProperties: true
        xhr:
          type: boolean
          default: false
        extract:
          type: object
          additionalProperties: true
        headers:
          type: object
          additionalProperties:
            type: string
        cookies:
          type: object
          additionalProperties:
            type: string
        mode:
          type: string
          enum: [full, summary]
          default: full
        ai_prompt:
          type: string
        ai_schema:
          type: object
    ScrapeEnvelope:
      allOf:
        - $ref: "#/components/schemas/Envelope"
        - type: object
          properties:
            payload:
              type: object
              properties:
                url:
                  type: string
                finalUrl:
                  type: string
                status:
                  type: integer
                title:
                  type: string
                format:
                  type: string
                content:
                  type: string
                bytes:
                  type: integer
                durationMs:
                  type: number
                engine:
                  type: string
                usage:
                  $ref: "#/components/schemas/Usage"
    SerpRequest:
      type: object
      properties:
        query:
          type: string
          description: Required except for ID-addressed verticals (place_details, product, flights, lens, reviews).
        engine:
          type: string
          enum: [google, bing, duckduckgo]
          default: google
        search_type:
          type: string
          default: search
          enum: [search, shopping, images, news, places, maps, videos, scholar, jobs, autocomplete, place_details, hotels, flights, events, product, lens, reviews, trends]
        type:
          type: string
          description: Alias of search_type (Google tbm/udm values accepted).
        country:
          type: string
        gl:
          type: string
          description: Alias of country.
        lang:
          type: string
        hl:
          type: string
          description: Alias of lang.
        num:
          type: number
          default: 10
          minimum: 1
          maximum: 100
        page:
          type: number
          default: 1
        start:
          type: number
        device:
          type: string
          enum: [desktop, mobile]
          default: desktop
        brd_mobile:
          type: integer
          description: Alias of device mobile when set to 1.
        render:
          type: boolean
        location:
          type: string
        uule:
          type: string
        safe:
          type: string
          enum: [active, off]
        nfpr:
          type: boolean
        google_params:
          type: object
          additionalProperties:
            type: string
        place_id:
          type: string
        data_id:
          type: string
        product_id:
          type: string
    SerpEnvelope:
      allOf:
        - $ref: "#/components/schemas/Envelope"
        - type: object
          properties:
            payload:
              type: object
              properties:
                organic:
                  type: array
                  items:
                    type: object
                usage:
                  $ref: "#/components/schemas/Usage"
    MapRequest:
      type: object
      required: [url]
      properties:
        url:
          type: string
        limit:
          type: number
          default: 100
          maximum: 5000
        search:
          type: string
        includeSubdomains:
          type: boolean
          default: false
        sitemapOnly:
          type: boolean
          default: false
        group_by:
          type: string
          enum: [path]
        country:
          type: string
    MapEnvelope:
      allOf:
        - $ref: "#/components/schemas/Envelope"
        - type: object
          properties:
            payload:
              type: object
              properties:
                links:
                  type: array
                  items:
                    type: string
                count:
                  type: integer
                total:
                  type: integer
                usage:
                  $ref: "#/components/schemas/Usage"
    CrawlRequest:
      type: object
      required: [url]
      properties:
        url:
          type: string
        limit:
          type: number
          default: 50
          maximum: 500
        depth:
          type: number
          default: 3
          maximum: 10
        format:
          type: string
          enum: [markdown, html, text]
          default: markdown
        contentMode:
          type: string
          enum: [smart, article, full]
          default: smart
        render:
          type: boolean
          default: false
        sameDomain:
          type: boolean
          default: true
        allowSubdomains:
          type: boolean
          default: false
        include:
          type: array
          items:
            type: string
        exclude:
          type: array
          items:
            type: string
        country:
          type: string
    CrawlStartEnvelope:
      allOf:
        - $ref: "#/components/schemas/Envelope"
        - type: object
          properties:
            payload:
              type: object
              properties:
                id:
                  type: string
                status:
                  type: string
                  enum: [running, completed, failed, cancelled]
                seed:
                  type: string
                statusUrl:
                  type: string
    CrawlPollEnvelope:
      allOf:
        - $ref: "#/components/schemas/Envelope"
    BatchRequest:
      type: object
      required: [urls]
      properties:
        urls:
          type: array
          maxItems: 5000
          items:
            type: string
            maxLength: 2048
        format:
          type: string
          enum: [markdown, html, text]
          default: markdown
        engine:
          type: string
          enum: [auto, tls, fetch, render]
          default: auto
        render:
          type: boolean
          default: false
        extract:
          type: object
          additionalProperties: true
        contentMode:
          type: string
          enum: [smart, article, full]
          default: smart
        mode:
          type: string
          enum: [full, summary]
          default: full
        concurrency:
          type: number
          default: 5
          maximum: 20
        webhook:
          type: string
        country:
          type: string
    SeoAuditRequest:
      type: object
      required: [url]
      properties:
        url:
          type: string
        no_render:
          type: boolean
          default: false
        noRender:
          type: boolean
        country:
          type: string
    SeoAuditEnvelope:
      allOf:
        - $ref: "#/components/schemas/Envelope"
    CollectorRunRequest:
      type: object
      additionalProperties: true
      properties:
        max_results:
          type: integer
        country:
          type: string
        lang:
          type: string
        async:
          type: boolean
          default: false
    CollectorRunEnvelope:
      allOf:
        - $ref: "#/components/schemas/Envelope"
        - type: object
          properties:
            payload:
              type: object
              properties:
                run_id:
                  type: string
                slug:
                  type: string
                status:
                  type: string
                count:
                  type: integer
                results:
                  type: array
                  items:
                    type: object
                usage:
                  $ref: "#/components/schemas/Usage"
    PlanType:
      type: string
      enum:
        - residentialbasic
        - residentialpremium
        - resiprivate
        - isp
        - datacenter
        - datacentertraffic
        - ipv6
        - mobile
        - mobile_v2
    GenerateProxiesRequest:
      type: object
      required: [orderId]
      properties:
        orderId:
          type: string
          description: From GET /v1/public/proxies.
        protocol:
          type: string
          enum: [http, socks5]
          default: http
        format:
          type: string
          enum:
            - "user:pass@host:port"
            - "host:port:user:pass"
            - "http://user:pass@host:port"
            - "socks5://user:pass@host:port"
          default: "user:pass@host:port"
        quantity:
          type: integer
          minimum: 1
          maximum: 10000
          default: 10
        country:
          type: string
          description: Country code, lowercase, e.g. us.
        state:
          type: string
          description: Region slug (Residential Premium and Mobile V2). Alias region.
        region:
          type: string
        city:
          type: string
        rotation:
          type: string
          enum: [rotating, sticky, static]
          default: rotating
          description: static is IPv6 only.
        sessionTime:
          type: integer
          minimum: 1
          maximum: 1440
          default: 10
          description: Sticky duration in minutes. Residential Basic/Datacenter minimum 3.
        isp:
          type: string
          description: Residential Premium / Mobile V2 ISP code from the location tree.
        asn:
          type: string
          description: Residential/Datacenter Basic, e.g. AS12345.
        strict:
          type: boolean
          description: Residential/Datacenter Basic — allow nearby fallback.
        filter:
          type: string
          enum: [speed, speed-quality, quality]
        ip:
          type: string
          description: Mobile V2 — a whitelisted IP to fetch the IP-auth list instead of user:pass.
        gateway:
          type: string
          enum: [ww, us, eu, as]
          default: ww
        isExtension:
          type: boolean
          default: false
          description: Unique hostname prefix for Chrome extension cache-busting.
    GenerateProxiesEnvelope:
      allOf:
        - $ref: "#/components/schemas/Envelope"
        - type: object
          properties:
            payload:
              type: object
              properties:
                proxies:
                  type: array
                  items:
                    type: string
    WhitelistIpRequest:
      type: object
      required: [orderId, ip]
      properties:
        orderId:
          type: string
        ip:
          type: string
          description: IPv4 to whitelist.
        action:
          type: string
          enum: [add, update]
          description: Mobile — update edits an existing entry.
        ports_count:
          type: integer
          minimum: 1
          maximum: 1000
        protocol:
          type: string
          enum: [HTTP, SOCKS5]
        country:
          type: string
        region:
          type: string
        city:
          type: string
        isp:
          type: string
        sticky:
          type: boolean
        ttl:
          type: integer
          description: Mobile sticky TTL in seconds.