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

# Search the web

> Search the web with a normalized response contract.

Requires `web:search` through a scoped Pirehub API key or OAuth token.
See [Web data](/guides/web-data) for request limits, response fields, crawl lifecycle
and error handling. See [Authentication](/api-reference/authentication)
for credential headers and quota rules.


## OpenAPI

````yaml api-reference/openapi.json POST /api/v1/web/search
openapi: 3.1.0
info:
  title: Pirehub by ntanduy API
  description: >-
    Scoped developer APIs from ntanduy for domain and IP intelligence, secured
    media processing, and Vietnamese bank QR utilities.
  version: 1.0.0
servers:
  - url: https://www.ntanduy.com
    description: Production
security: []
tags:
  - name: Screenshot tools
    description: Capture public webpages as PNG images.
  - name: Social embeds
    description: Resolve public social URLs into normalized oEmbed data.
  - name: Domains
    description: Domain registration, WHOIS, and DNS resolution intelligence.
  - name: IP information
    description: >-
      Normalized location, network, and timezone information for public IP
      addresses.
  - name: Video tools
    description: Video metadata and secure download-option extraction.
  - name: Payment utilities
    description: Scoped APIs for generating interoperable bank-transfer artifacts.
  - name: Free subdomains
    description: >-
      First-party dashboard routes requiring a Clerk session and automatic
      website verification for namespace registration, desired DNS state,
      asynchronous provider operations, and DNS propagation verification.
  - name: Web data
    description: Search, extract, map and crawl public web content.
paths:
  /api/v1/web/search:
    post:
      tags:
        - Web data
      summary: Search the web
      description: >-
        Requires web:search via an issued API key or OAuth token; browser
        sessions alone are not accepted. JSON bodies are limited to 16 KiB and
        reject unknown fields. Responses use the common envelope and are not
        cached. See the Web data guide for provider-independent semantics and
        limits.
      operationId: searchWeb
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebSearchRequest'
            example:
              query: example
              limit: 5
      responses:
        '200':
          description: Successful operation.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
            Cache-Control:
              schema:
                type: string
                const: private, no-store
            X-ApiKey-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-ApiKey-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-ApiKey-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
            X-ApiKey-RateLimit-Window:
              $ref: '#/components/headers/ApiRateWindow'
            X-ApiKey-Monthly-Limit:
              $ref: '#/components/headers/MonthlyLimit'
            X-ApiKey-Monthly-Remaining:
              $ref: '#/components/headers/MonthlyRemaining'
            X-ApiKey-Monthly-Reset:
              $ref: '#/components/headers/MonthlyReset'
            X-ApiKey-Credit-Cost:
              $ref: '#/components/headers/ApiCreditCost'
            X-ApiKey-Credit-Limit:
              $ref: '#/components/headers/ApiCreditLimit'
            X-ApiKey-Credit-Reset:
              $ref: '#/components/headers/ApiCreditReset'
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required:
                  - success
                  - data
                properties:
                  success:
                    const: true
                  data:
                    $ref: '#/components/schemas/WebSearchResult'
              example:
                success: true
                data:
                  query: example
                  results:
                    - url: https://example.com/
                      title: Example Domain
                      description: null
                      content: null
                      contentFormat: null
                      contentTruncated: false
                      httpStatus: null
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '409':
          description: Duplicate crawl request or idempotency conflict.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: JSON body exceeds 16 KiB.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '415':
          description: Content-Type must be application/json.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimitError'
        '502':
          $ref: '#/components/responses/UpstreamBadResponse'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/UpstreamTimeout'
      security:
        - pirehubOAuth:
            - web:search
        - pirehubApiKey: []
        - bearerApiKey: []
components:
  schemas:
    WebSearchRequest:
      type: object
      additionalProperties: false
      required:
        - query
      properties:
        query:
          type: string
          minLength: 1
          maxLength: 1000
        limit:
          type: integer
          minimum: 1
          maximum: 10
          default: 5
    WebSearchResult:
      type: object
      additionalProperties: false
      required:
        - query
        - results
      properties:
        query:
          type: string
        results:
          type: array
          items:
            $ref: '#/components/schemas/WebDocument'
    ErrorResponse:
      type: object
      additionalProperties: false
      required:
        - success
        - error
        - data
      properties:
        success:
          type: boolean
          const: false
        error:
          $ref: '#/components/schemas/ErrorDetail'
        data:
          $ref: '#/components/schemas/ErrorData'
    WebDocument:
      type: object
      additionalProperties: false
      required:
        - url
        - title
        - description
        - content
        - contentFormat
        - contentTruncated
        - httpStatus
      properties:
        url:
          type: string
          format: uri
        title:
          type:
            - string
            - 'null'
        description:
          type:
            - string
            - 'null'
        content:
          type:
            - string
            - 'null'
          maxLength: 200000
        contentFormat:
          enum:
            - markdown
            - text
            - null
        contentTruncated:
          type: boolean
        httpStatus:
          type:
            - integer
            - 'null'
          description: Target HTTP status when available; null when not reported.
    ErrorDetail:
      type: object
      additionalProperties: false
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: Stable machine-readable error code.
        message:
          type: string
          description: Safe human-readable explanation.
    ErrorData:
      type: object
      description: >-
        Safe structured error metadata. Every JSON API error includes a
        correlation ID for support and log lookup.
      additionalProperties: true
      required:
        - requestId
      properties:
        requestId:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[A-Za-z0-9._:-]+$
          description: >-
            Opaque correlation ID for this request. Include it when contacting
            support.
          example: 1704b073-5bf1-4d64-8f9a-3af95edb3bf1
    RateLimitErrorResponse:
      type: object
      additionalProperties: false
      required:
        - success
        - error
        - data
      properties:
        success:
          type: boolean
          const: false
        error:
          type: object
          additionalProperties: false
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - RATE_LIMITED
                - MONTHLY_QUOTA_EXCEEDED
            message:
              type: string
        data:
          $ref: '#/components/schemas/RateLimitErrorData'
    RateLimitErrorData:
      type: object
      additionalProperties: false
      required:
        - requestId
      properties:
        reason:
          type: string
          enum:
            - per_minute
            - monthly_quota
          description: >-
            Present for API-key limits. Browser/IP and upstream limits may omit
            it.
        limit:
          type: integer
          minimum: 0
          description: Configured API-key limit for the applicable window.
        remaining:
          type: integer
          minimum: 0
          description: Requests remaining in the applicable API-key window.
        resetAt:
          type: string
          format: date-time
          description: ISO 8601 reset time for the applicable API-key window.
        retryAfter:
          type: integer
          minimum: 1
          description: Whole seconds to wait before retrying when supplied by the limiter.
        requestId:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[A-Za-z0-9._:-]+$
          description: Opaque correlation ID for this request.
  headers:
    RequestId:
      description: >-
        Opaque correlation ID for this request. It matches `data.requestId` on
        JSON error responses.
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[A-Za-z0-9._:-]+$
    RateLimitLimit:
      description: >-
        Maximum requests in the configured window (admin keys: 60 seconds;
        self-service: 1 second).
      schema:
        type: integer
    RateLimitRemaining:
      description: Estimated requests remaining in the configured rate window.
      schema:
        type: integer
    RateLimitReset:
      description: Rate window reset time as Unix seconds.
      schema:
        type: integer
    ApiRateWindow:
      description: >-
        Self-service keys only: 1 second. Administrator keys use a 60-second
        window.
      schema:
        type: integer
        minimum: 1
    MonthlyLimit:
      description: Administrator-issued keys only. Monthly request limit, or `unlimited`.
      schema:
        oneOf:
          - type: integer
          - type: string
            const: unlimited
    MonthlyRemaining:
      description: >-
        Administrator-issued keys only. Requests remaining this month, or
        `unlimited`.
      schema:
        oneOf:
          - type: integer
          - type: string
            const: unlimited
    MonthlyReset:
      description: >-
        Administrator-issued keys only. Monthly quota reset time as Unix
        seconds.
      schema:
        type: integer
    ApiCreditCost:
      description: >-
        Self-service keys only: reserved action credits; consumption occurs on
        success.
      schema:
        type: integer
        minimum: 1
    ApiCreditLimit:
      description: 'Self-service keys only: owner subscription-period credit allowance.'
      schema:
        type: integer
        minimum: 1
    ApiCreditReset:
      description: 'Self-service keys only: subscription period end as Unix seconds.'
      schema:
        type: integer
        minimum: 1
    RetryAfter:
      description: Seconds to wait before retrying.
      schema:
        type: integer
        minimum: 0
    ServerTiming:
      description: >-
        Optional server timing diagnostics in milliseconds. API-key responses
        may expose parse, lookup, verify, scope, runtime_snapshot, quota,
        credit_reserve, provider, credit_finalize, pre-provider and total
        durations using *_ms metric names. For paid keys, api_key_lookup_ms
        includes the fresh credential, owner, subscription, entitlement and
        capability read; runtime_snapshot_ms covers request parsing and snapshot
        validation. Admission stages overlap with api_key_pre_provider_ms;
        provider_ms includes endpoint validation and result-cache access.
        Inapplicable stages are omitted. Background logging and client network
        time are excluded. api_key_cache describes the lookup path.
      schema:
        type: string
  responses:
    ValidationError:
      description: The domain or request body is invalid.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: VALIDATION_ERROR
              message: Invalid domain format or missing required fields
            data:
              requestId: 1704b073-5bf1-4d64-8f9a-3af95edb3bf1
    UnauthorizedError:
      description: The API key or OAuth access token is missing or invalid.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            invalidKey:
              value:
                success: false
                error:
                  code: UNAUTHORIZED
                  message: A valid API key is required.
                data:
                  requestId: 1704b073-5bf1-4d64-8f9a-3af95edb3bf1
    ForbiddenError:
      description: >-
        The API key is revoked, expired, or lacks the required scope; or browser
        request verification is required or failed. Self-service keys can also
        return API_PLAN_REQUIRED or API_CAPABILITY_DENIED.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            missingScope:
              value:
                success: false
                error:
                  code: FORBIDDEN
                  message: This API key does not have permission for this endpoint.
                data:
                  requestId: 1704b073-5bf1-4d64-8f9a-3af95edb3bf1
            revokedKey:
              value:
                success: false
                error:
                  code: FORBIDDEN
                  message: This API key has been revoked.
                data:
                  requestId: 1704b073-5bf1-4d64-8f9a-3af95edb3bf1
            expiredKey:
              value:
                success: false
                error:
                  code: FORBIDDEN
                  message: This API key has expired.
                data:
                  requestId: 1704b073-5bf1-4d64-8f9a-3af95edb3bf1
            captchaFailed:
              value:
                success: false
                error:
                  code: CAPTCHA_FAILED
                  message: Request verification failed. Please try again.
                data:
                  requestId: 1704b073-5bf1-4d64-8f9a-3af95edb3bf1
            captchaRequired:
              value:
                success: false
                error:
                  code: CAPTCHA_REQUIRED
                  message: Request verification is required.
                data:
                  requestId: 1704b073-5bf1-4d64-8f9a-3af95edb3bf1
    RateLimitError:
      description: >-
        The API key exceeded its per-minute or monthly quota, the first-party
        browser request exceeded its per-IP limit, or an upstream provider
        throttled the request. Self-service keys can also return
        API_CREDITS_EXCEEDED or USAGE_CONCURRENCY_EXCEEDED; denied requests
        consume no credits.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
        X-ApiKey-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-ApiKey-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-ApiKey-RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
        X-ApiKey-Monthly-Limit:
          $ref: '#/components/headers/MonthlyLimit'
        X-ApiKey-Monthly-Remaining:
          $ref: '#/components/headers/MonthlyRemaining'
        X-ApiKey-Monthly-Reset:
          $ref: '#/components/headers/MonthlyReset'
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
        Server-Timing:
          $ref: '#/components/headers/ServerTiming'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/RateLimitErrorResponse'
          examples:
            perMinute:
              summary: API-key per-minute limit exceeded
              value:
                success: false
                error:
                  code: RATE_LIMITED
                  message: Too many requests. Please try again later.
                data:
                  reason: per_minute
                  limit: 60
                  remaining: 0
                  resetAt: '2026-08-20T07:11:00.000Z'
                  retryAfter: 23
                  requestId: 1704b073-5bf1-4d64-8f9a-3af95edb3bf1
            monthlyQuota:
              summary: API-key monthly quota exhausted
              value:
                success: false
                error:
                  code: MONTHLY_QUOTA_EXCEEDED
                  message: Monthly API request quota has been exhausted.
                data:
                  reason: monthly_quota
                  limit: 10
                  remaining: 0
                  resetAt: '2026-09-01T00:00:00.000Z'
                  retryAfter: 1010458
                  requestId: 1f4f2b4a-da4f-47d2-be0e-f85ddaeab6d4
            browserPerIp:
              summary: First-party browser per-IP limit exceeded
              value:
                success: false
                error:
                  code: RATE_LIMITED
                  message: Too many requests. Please try again later.
                data:
                  retryAfter: 12
                  requestId: 1704b073-5bf1-4d64-8f9a-3af95edb3bf1
            upstreamRateLimit:
              summary: Upstream provider rate limited the lookup
              value:
                success: false
                error:
                  code: RATE_LIMITED
                  message: Too many requests. Please try again later.
                data:
                  requestId: 1704b073-5bf1-4d64-8f9a-3af95edb3bf1
    UpstreamBadResponse:
      description: An upstream provider returned an invalid or unusable response.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: UPSTREAM_BAD_RESPONSE
              message: Bad gateway. The upstream service returned an invalid response.
            data:
              requestId: 1704b073-5bf1-4d64-8f9a-3af95edb3bf1
    ServiceUnavailable:
      description: >-
        The requested service or one of its dependencies is temporarily
        unavailable.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            dependencyUnavailable:
              value:
                success: false
                error:
                  code: SERVICE_UNAVAILABLE
                  message: Service temporarily unavailable. Please try again later.
                data:
                  requestId: 1704b073-5bf1-4d64-8f9a-3af95edb3bf1
            quotaServiceUnavailable:
              value:
                success: false
                error:
                  code: SERVICE_UNAVAILABLE
                  message: API key quota service is temporarily unavailable.
                data:
                  retryAfter: 2
                  requestId: 1704b073-5bf1-4d64-8f9a-3af95edb3bf1
    UpstreamTimeout:
      description: The requested upstream dependency exceeded its deadline.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: UPSTREAM_TIMEOUT
              message: Gateway timeout. The upstream service did not respond in time.
            data:
              requestId: 1704b073-5bf1-4d64-8f9a-3af95edb3bf1
  securitySchemes:
    pirehubOAuth:
      type: oauth2
      description: >-
        OAuth 2.0 Authorization Code with PKCE, issued by Pirehub's Clerk
        authorization server. Request only the scopes required by the operation.
      flows:
        authorizationCode:
          authorizationUrl: https://clerk.ntanduy.com/oauth/authorize
          tokenUrl: https://clerk.ntanduy.com/oauth/token
          refreshUrl: https://clerk.ntanduy.com/oauth/token
          scopes:
            domain:whois: Read normalized public WHOIS data.
            domain:dns: Read DNS records for a hostname.
            ip:lookup: Read allowlisted public IP information.
            video:extract: Read normalized metadata for a public video URL.
            screenshot:capture: Capture a public webpage as a PNG image.
            oembed:read: Resolve a public social URL to oEmbed data.
            bank-qr:generate: Generate a Vietnamese bank transfer QR payload and SVG.
            bank-qr:banks: Read the supported bank QR institution directory.
            web:search: Search the web.
            web:scrape: Extract public webpage content.
            web:map: Discover website URLs.
            web:crawl: Submit, read and cancel website crawls.
    pirehubApiKey:
      type: apiKey
      in: header
      name: x-pirehub-api-key
      description: A scoped Pirehub API key. Each operation documents its required scope.
    bearerApiKey:
      type: http
      scheme: bearer
      bearerFormat: Pirehub API key
      description: A Pirehub API key beginning with `ph_live_`.

````

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