openapi: 3.1.0
info:
  title: Zelnum Detection API
  version: 1.0.0
  description: |
    Machine-readable contract for the two detection families exposed by Zelnum.
    Realtime endpoints are synchronous and accept one phone number or email at a time.
    Bulk endpoints create an asynchronous task for a phone or email list.

    ## Rate limits

    | Endpoint group | Limit |
    | --- | --- |
    | `GET /v1/products`, `GET /v1/balance`, `GET /v1/api-key`, `GET /v1/tasks/{token}`, `GET /v1/tasks/{token}/result`, `GET /v1/tasks/{token}/download` | 60 req/min per API key (120 / minute per IP) |
    | `POST /v1/detect`, `POST /v1/detect/file`, `POST /v1/quotes/bulk` | 10 req/min per API key (30 / minute per IP) |
    | `POST /v1/realtime/quote/{series}`, `POST /v1/realtime/check/{series}`, `POST /v1/realtime/detect` | 10 req/min per API key (30 / minute per IP) |

    Token bucket per API key: burst 60, refill 1/s. Per account: burst 120, refill 2/s.
    The IP layer allows 180 req/min. Every response carries `X-RateLimit-Remaining`.
    A throttled request returns `429` with a `Retry-After` header (realtime responses also
    include a `retry_after` field in the body). Resume with exponential backoff.

    ## Errors

    Business errors are returned as `{ "code": "...", "message": "..." }` and are safe to
    branch on: the `code` enum below is exhaustive. Malformed request parameters (framework
    validation) and unknown routes return the standard framework shape
    `{ "message": "...", "errors": { ... } }` instead.

    ---

    ZelNum is a phone, email and messaging-app verification API for list cleaning, list hygiene and fraud review. See [ZelNum](https://zelnum.com/) for product coverage, pricing and limits.
  termsOfService: https://zelnum.com/terms/
  contact:
    name: ZelNum API Support
    url: https://zelnum.com/contact/
  license:
    name: Commercial API — see terms of service
    url: https://zelnum.com/terms/
  x-canonical-spec: https://zelnum.com/openapi.yaml
  x-generated-by: tools/sync-openapi.mjs
  x-mirrored-from: https://app.zelnum.com/docs/openapi.yaml
servers:
  - url: https://api.zelnum.com/api/v1
    description: Production API base URL. The hosted documentation may override this per environment.
tags:
  - name: Common
    description: Account, catalog and task-result endpoints.
  - name: Realtime
    description: Synchronous single-identifier checks, billed per check.
  - name: Bulk
    description: Asynchronous list submission, polling and result download.
  - name: Quotes
    description: Cost estimates returned before a submission is created.
paths:
  /products:
    get:
      tags:
        - Common
      summary: List active products
      security: []
      responses:
        "200":
          description: Active product catalog
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Product"
          headers:
            X-RateLimit-Remaining:
              description: Requests left in the current window for this API key. Present on every response.
              schema:
                type: integer
      description: "Public product catalog — **no API key required**. Returns every active realtime and bulk product together with its series and minimum quantity. Read it before submitting: bulk endpoints take the slug, realtime endpoints take the numeric id."
      x-example-ids:
        realtime_phone: 47
        realtime_slug: wavalid-realtime
        bulk_slug: hlr-lookup
        bulk_id: 38
  /balance:
    get:
      tags:
        - Common
      summary: Get account balance
      security:
        - ApiKeyHeader: []
        - BearerAuth: []
      responses:
        "200":
          description: Current USD balance
          content:
            application/json:
              schema:
                type: object
                required:
                  - balance
                properties:
                  balance:
                    type: string
                    example: "5.230000"
          headers:
            X-RateLimit-Remaining:
              description: Requests left in the current window for this API key. Present on every response.
              schema:
                type: integer
        "401":
          $ref: "#/components/responses/Unauthorized"
  /api-key:
    get:
      tags:
        - Common
      summary: Get API key metadata
      security:
        - ApiKeyHeader: []
        - BearerAuth: []
      responses:
        "200":
          description: Masked API key metadata
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiKeyMetadata"
          headers:
            X-RateLimit-Remaining:
              description: Requests left in the current window for this API key. Present on every response.
              schema:
                type: integer
        "401":
          $ref: "#/components/responses/Unauthorized"
  /realtime/check/{series}:
    post:
      tags:
        - Realtime
      summary: Check one identifier in realtime
      parameters:
        - name: series
          in: path
          required: true
          description: Product series. Must match the series of the selected products.
          schema:
            type: string
            enum:
              - phone
              - email
        - name: Idempotency-Key
          in: header
          required: true
          description: Client-generated unique key, max 191 bytes, scoped to the account. Replaying the same key with the same payload does not charge twice; a different payload on the same key returns 409 `idempotency_conflict`.
          schema:
            type: string
            maxLength: 191
      security:
        - ApiKeyHeader: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RealtimeRequest"
      responses:
        "200":
          description: Succeeded or partially succeeded combo
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RealtimeResponse"
          headers:
            X-RateLimit-Remaining:
              description: Requests left in the current window for this API key. Present on every response.
              schema:
                type: integer
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "409":
          $ref: "#/components/responses/Conflict"
        "422":
          $ref: "#/components/responses/ValidationError"
        "429":
          $ref: "#/components/responses/RateLimited"
        "503":
          $ref: "#/components/responses/Unavailable"
      description: "Synchronously checks one phone number or email against **realtime** products from a single series. Use realtime product **ids**, not bulk slugs. Each selected product is billed at its snapshotted per-check price, including cache hits; only `valid` and `invalid` results are charged, while `unknown`, timeout and busy items are refunded. Replaying an `Idempotency-Key` never double-charges. A partially successful combo still returns HTTP 200 with `status: partial_succeeded` — inspect each item's `billing_status` and `error_code` instead of trusting the status code alone."
  /realtime/detect:
    post:
      tags:
        - Realtime
      summary: Legacy realtime endpoint
      description: Legacy compatibility endpoint retained for pre-release clients. New integrations must use `POST /realtime/check/{series}`. Same request and response shapes as the current endpoint.
      deprecated: true
      security:
        - ApiKeyHeader: []
        - BearerAuth: []
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          description: Client-generated unique key, max 191 bytes, scoped to the account. Replaying the same key with the same payload does not charge twice; a different payload on the same key returns 409 `idempotency_conflict`.
          schema:
            type: string
            maxLength: 191
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RealtimeRequest"
      responses:
        "200":
          description: Realtime response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RealtimeResponse"
          headers:
            X-RateLimit-Remaining:
              description: Requests left in the current window for this API key. Present on every response.
              schema:
                type: integer
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationError"
  /detect:
    post:
      tags:
        - Bulk
      summary: Submit a bulk detection task
      security:
        - ApiKeyHeader: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BulkRequest"
      responses:
        "202":
          description: Task queued
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AsyncSubmitResponse"
          headers:
            X-RateLimit-Remaining:
              description: Requests left in the current window for this API key. Present on every response.
              schema:
                type: integer
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/InsufficientBalance"
        "422":
          $ref: "#/components/responses/ValidationError"
      description: Submits a phone or email array for asynchronous detection. Cost is pre-deducted at submission time and unchecked rows are refunded once the task completes. The array must meet the selected product's `min_phones` and stay under the platform maximum (2,000,000 identifiers by default). Phone products require `country`; email products must omit it.
  /detect/file:
    post:
      tags:
        - Bulk
      summary: Submit a bulk detection file
      security:
        - ApiKeyHeader: []
        - BearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - product
                - file
              properties:
                product:
                  type: string
                  description: Product slug from `GET /products`.
                country:
                  type: string
                  description: ISO 3166-1 alpha-2 code. Required for phone products; omit for email products.
                file:
                  type: string
                  format: binary
                  description: TXT or CSV file, one phone number or email per line. Maximum 50 MB.
      responses:
        "202":
          description: Task queued
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AsyncSubmitResponse"
          headers:
            X-RateLimit-Remaining:
              description: Requests left in the current window for this API key. Present on every response.
              schema:
                type: integer
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationError"
      description: Uploads a TXT or CSV file for asynchronous detection, one identifier per line, maximum 50 MB.
  /tasks/{token}:
    get:
      tags:
        - Bulk
      summary: Get task status
      security:
        - ApiKeyHeader: []
        - BearerAuth: []
      parameters:
        - name: token
          in: path
          required: true
          description: Task token returned by `POST /detect` or `POST /detect/file`.
          schema:
            type: string
      responses:
        "200":
          description: Current task status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskStatus"
          headers:
            X-RateLimit-Remaining:
              description: Requests left in the current window for this API key. Present on every response.
              schema:
                type: integer
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
      description: "Polls task progress. Recommended interval: 3–5 seconds, with backoff. `status` moves through `pending` → `processing` → `completed` | `failed`."
  /tasks/{token}/result:
    get:
      tags:
        - Bulk
      summary: Get result download URL
      security:
        - ApiKeyHeader: []
        - BearerAuth: []
      parameters:
        - name: token
          in: path
          required: true
          description: Task token returned by `POST /detect` or `POST /detect/file`.
          schema:
            type: string
      responses:
        "200":
          description: Result metadata and download URL
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaskResult"
          headers:
            X-RateLimit-Remaining:
              description: Requests left in the current window for this API key. Present on every response.
              schema:
                type: integer
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ResultNotReady"
      description: "Returns the result metadata and a download URL. Call it only after the task reports `result_available: true`; otherwise the API returns 422 `result_not_ready`. Results expire — honour `expires_at`."
  /tasks/{token}/download:
    get:
      tags:
        - Bulk
      summary: Download task result
      security:
        - ApiKeyHeader: []
        - BearerAuth: []
      parameters:
        - name: token
          in: path
          required: true
          description: Task token returned by `POST /detect` or `POST /detect/file`.
          schema:
            type: string
      responses:
        "200":
          description: Result archive or CSV stream
          content:
            application/zip:
              schema:
                type: string
                format: binary
            text/csv:
              schema:
                type: string
                format: binary
          headers:
            X-RateLimit-Remaining:
              description: Requests left in the current window for this API key. Present on every response.
              schema:
                type: integer
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
      description: Streams the result archive (ZIP) or CSV. Same expiry window as `/tasks/{token}/result`.
  /realtime/quote/{series}:
    post:
      tags:
        - Quotes
      summary: Quote one realtime check
      description: |-
        Returns the cost of a realtime check before it is submitted, so integrators can pre-flight a combo without reserving balance. Shares the realtime rate-limit group (10 req/min per key).

        **Upstream documentation gap.** This endpoint exists and is rate limited, but neither the interactive docs nor the generated spec define its request/response body yet. The shapes below are intentionally left open; check https://app.zelnum.com/docs/en or contact support before relying on specific fields.
      x-zelnum-doc-status: upstream-documentation-gap
      security:
        - ApiKeyHeader: []
        - BearerAuth: []
      parameters:
        - name: series
          in: path
          required: true
          description: Product series. Must match the series of the selected products.
          schema:
            type: string
            enum:
              - phone
              - email
      requestBody:
        required: true
        description: Quote request. Presumed to mirror the realtime check payload.
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Estimated cost for the requested combo.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
          headers:
            X-RateLimit-Remaining:
              description: Requests left in the current window for this API key. Present on every response.
              schema:
                type: integer
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationError"
        "429":
          $ref: "#/components/responses/RateLimited"
  /quotes/bulk:
    post:
      tags:
        - Quotes
      summary: Quote a bulk submission
      description: |-
        Returns the cost of a bulk submission before it is created, so integrators can pre-flight large lists without pre-deducting balance. Shares the heavy bulk rate-limit group (10 req/min per key).

        **Upstream documentation gap.** This endpoint exists and is rate limited, but neither the interactive docs nor the generated spec define its request/response body yet. The shapes below are intentionally left open; check https://app.zelnum.com/docs/en or contact support before relying on specific fields.
      x-zelnum-doc-status: upstream-documentation-gap
      security:
        - ApiKeyHeader: []
        - BearerAuth: []
      requestBody:
        required: true
        description: Quote request. Presumed to mirror the bulk submission payload.
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Estimated cost for the submitted list.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
          headers:
            X-RateLimit-Remaining:
              description: Requests left in the current window for this API key. Present on every response.
              schema:
                type: integer
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationError"
        "429":
          $ref: "#/components/responses/RateLimited"
components:
  securitySchemes:
    ApiKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: ZelnumApiKey
  schemas:
    Product:
      type: object
      required:
        - id
        - slug
        - name
        - mode
        - series
        - min_phones
      properties:
        id:
          type: integer
          example: 47
          description: Numeric product id. Realtime endpoints accept these; bulk endpoints use the slug.
        slug:
          type: string
          example: wavalid-realtime
          description: Product slug. Naming is not fully uniform upstream — realtime slugs usually end in `-rt` (for example `tgValid-rt`) with `wavalid-realtime` as the WhatsApp exception; bulk slugs are unprefixed (for example `hlr-lookup`). Treat the value from `GET /products` as authoritative.
          examples:
            - wavalid-realtime
            - hlr-lookup
        name:
          type: string
          description: Product display name. `name_translations` carries localized names.
        name_translations:
          type:
            - object
            - "null"
          additionalProperties:
            type: string
        mode:
          type: string
          enum:
            - realtime
            - async
        series:
          type: string
          enum:
            - phone
            - email
        min_phones:
          type: integer
          minimum: 1
          description: Minimum number of identifiers this product accepts per submission. Email products typically start at 1,000; bulk phone products at 500–2,000.
      description: "An active product you can submit work for. Always read `GET /products` before submitting: the catalog is the source of truth for slugs, series and per-product minimums."
    RealtimeRequest:
      type: object
      required:
        - identifier
        - product_ids
      properties:
        identifier:
          type: string
          example: "+8613800138000"
          description: One E.164 phone number (for example `+8613800138000`) or one email address.
        country:
          type:
            - string
            - "null"
          example: CN
          description: Phone series only. Optional when `identifier` is a complete E.164 number; a national-format number requires the matching ISO 3166-1 alpha-2 code. Omit for the email series.
        product_ids:
          type: array
          minItems: 1
          maxItems: 10
          items:
            type: integer
            description: Realtime product id.
          description: 1–10 **realtime** product ids from the same series as the path. Bulk slugs are rejected with 422.
    RealtimeResponse:
      type: object
      required:
        - request_id
        - status
        - replayed
        - total_reserved
        - total_charged
        - total_refunded
        - results
      properties:
        request_id:
          type: string
          format: uuid
        identifier:
          type: string
        series:
          type: string
          enum:
            - phone
            - email
        status:
          type: string
          enum:
            - succeeded
            - partial_succeeded
            - failed
          description: Combo-level outcome. `partial_succeeded` and `failed` still return HTTP 200.
        request_status:
          type: string
        replayed:
          type: boolean
          description: True when this response is an idempotent replay of a stored request.
        total_reserved:
          type: number
          description: Amount reserved up front. `total_reserved = total_charged + total_refunded`.
        total_charged:
          type: number
          description: Amount actually charged.
        total_refunded:
          type: number
          description: Amount refunded for unknown, timed-out or busy items.
        charged_amount:
          type: number
        refunded_amount:
          type: number
        results:
          type: array
          items:
            $ref: "#/components/schemas/RealtimeResult"
      additionalProperties: true
    BulkRequest:
      type: object
      required:
        - product
        - phones
      properties:
        product:
          type: string
          description: Product slug from `GET /products` (bulk endpoints use slugs, not ids).
        country:
          type: string
          description: ISO 3166-1 alpha-2 code. Required for phone products; must be omitted for email products.
        phones:
          type: array
          minItems: 1
          items:
            type: string
          description: Identifiers to check. Must satisfy the product's `min_phones` and stay under the platform maximum.
      description: Bulk submission payload. Minimum and maximum counts come from the product definition.
    AsyncSubmitResponse:
      type: object
      required:
        - mode
        - task_id
      properties:
        mode:
          type: string
          enum:
            - async
        task_id:
          type: string
        message:
          type: string
        summary:
          type: object
          additionalProperties: true
          description: Submission summary. `total` is the accepted identifier count and `cost_deducted` the pre-deducted amount.
          properties:
            total:
              type: integer
            cost_deducted:
              type: number
        idempotent_replay:
          type: boolean
          description: True when this submission replays a previously accepted request instead of creating a new task.
        recovery_pending:
          type: boolean
          description: True when the platform is still reconciling a previously interrupted submission.
      description: Accepted task envelope returned by `POST /detect` and `POST /detect/file`.
    TaskStatus:
      type: object
      properties:
        task_id:
          type: string
        status:
          type: string
          enum:
            - pending
            - processing
            - completed
            - failed
        stage:
          type:
            - string
            - "null"
        stage_seconds:
          type:
            - integer
            - "null"
        last_progress_at:
          type:
            - string
            - "null"
          format: date-time
        failure_message:
          type:
            - string
            - "null"
        progress:
          type: integer
          minimum: 0
          maximum: 100
        product:
          type: string
        country:
          type:
            - string
            - "null"
        submitted:
          type: integer
        prefiltered:
          type: integer
        deduped:
          type: integer
        total:
          type: integer
        processed:
          type: integer
        valid:
          type: integer
        invalid:
          type: integer
        unknown:
          type: integer
        unknown_processed:
          type: integer
        unknown_unprocessed:
          type: integer
        cost_deducted:
          type: string
        cost_refunded:
          type: string
        result_available:
          type: boolean
        result_expires_at:
          type:
            - string
            - "null"
          format: date-time
        created_at:
          type: string
          format: date-time
    TaskResult:
      type: object
      required:
        - task_id
        - download_url
        - expires_at
        - summary
      properties:
        task_id:
          type: string
        download_url:
          type: string
          format: uri
        expires_at:
          type:
            - string
            - "null"
          format: date-time
        summary:
          type: object
          additionalProperties: true
    ApiKeyMetadata:
      type: object
      properties:
        api_key:
          type:
            - string
            - "null"
        message:
          type: string
        api_key_id:
          type: integer
        masked_key:
          type: string
        last_used:
          type:
            - string
            - "null"
          format: date-time
        created_at:
          type: string
          format: date-time
    Error:
      type: object
      properties:
        code:
          type: string
          enum:
            - unauthorized
            - account_banned
            - invalid_realtime_request
            - idempotency_key_required
            - invalid_idempotency_key
            - idempotency_conflict
            - request_in_progress
            - empty_input
            - product_not_found
            - product_mode_not_supported
            - invalid_country
            - too_few_numbers
            - too_many_numbers
            - insufficient_balance
            - realtime_disabled
            - realtime_rate_limited
            - product_pricing_unavailable
            - detection_capacity_unavailable
            - realtime_route_unavailable
            - realtime_fact_cache_unavailable
            - realtime_rate_limiter_unavailable
            - not_found
            - result_not_ready
            - result_expired
            - validation_failed
            - internal_error
          x-zelnum-http-status:
            unauthorized: "401"
            account_banned: "403"
            invalid_realtime_request: "400"
            idempotency_key_required: "400"
            invalid_idempotency_key: "400"
            idempotency_conflict: "409"
            request_in_progress: "409"
            empty_input: "422"
            product_not_found: "422"
            product_mode_not_supported: "422"
            invalid_country: "422"
            too_few_numbers: "422"
            too_many_numbers: "422"
            insufficient_balance: "402"
            realtime_disabled: "403"
            realtime_rate_limited: "429"
            product_pricing_unavailable: "422"
            detection_capacity_unavailable: "503"
            realtime_route_unavailable: "503"
            realtime_fact_cache_unavailable: "503"
            realtime_rate_limiter_unavailable: "503"
            not_found: "404"
            result_not_ready: "422"
            result_expired: "404"
            validation_failed: "422"
            internal_error: "500"
          description: Stable machine-readable error code.
        message:
          type: string
          description: Human-readable explanation. Not stable — do not branch on it.
        errors:
          type: object
          additionalProperties: true
      description: Business error envelope. `code` is a stable, exhaustive enum; branch on it rather than on the HTTP status alone.
      required:
        - code
        - message
    RealtimeResult:
      type: object
      description: One product's outcome inside a realtime response.
      required:
        - product_id
        - product
        - result
        - cached
        - billing_status
        - charged_amount
        - refunded_amount
      properties:
        product_id:
          type: integer
          description: Realtime product id used for this item.
        product:
          type: string
          description: Realtime product slug.
        result:
          type: string
          enum:
            - valid
            - invalid
            - unknown
          description: Verification signal.
        cached:
          type: boolean
          description: True when a previously stored result was reused. Cache hits are still billed.
        billing_status:
          type: string
          description: Per-item billing outcome, for example `charged` or `refunded`.
        charged_amount:
          type: number
          description: Amount charged for this item.
        refunded_amount:
          type: number
          description: Amount refunded for this item.
        checked_at:
          type:
            - string
            - "null"
          format: date-time
        error_code:
          type:
            - string
            - "null"
          description: Per-item failure code when the item could not be resolved. Failed items are not charged.
  responses:
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
      headers:
        X-RateLimit-Remaining:
          description: Requests left in the current window for this API key. Present on every response.
          schema:
            type: integer
    BadRequest:
      description: Malformed request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
      headers:
        X-RateLimit-Remaining:
          description: Requests left in the current window for this API key. Present on every response.
          schema:
            type: integer
    Conflict:
      description: Idempotency conflict or request still in progress
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
      headers:
        X-RateLimit-Remaining:
          description: Requests left in the current window for this API key. Present on every response.
          schema:
            type: integer
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
    ValidationError:
      description: Validation or product eligibility error
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
      headers:
        X-RateLimit-Remaining:
          description: Requests left in the current window for this API key. Present on every response.
          schema:
            type: integer
    ResultNotReady:
      description: Task is not complete or result is unavailable
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
      headers:
        X-RateLimit-Remaining:
          description: Requests left in the current window for this API key. Present on every response.
          schema:
            type: integer
    NotFound:
      description: Task or result was not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
      headers:
        X-RateLimit-Remaining:
          description: Requests left in the current window for this API key. Present on every response.
          schema:
            type: integer
    RateLimited:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
      headers:
        X-RateLimit-Remaining:
          description: Requests left in the current window for this API key. Present on every response.
          schema:
            type: integer
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
    InsufficientBalance:
      description: Insufficient account balance
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
      headers:
        X-RateLimit-Remaining:
          description: Requests left in the current window for this API key. Present on every response.
          schema:
            type: integer
    Unavailable:
      description: Detection capacity or route unavailable
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
      headers:
        X-RateLimit-Remaining:
          description: Requests left in the current window for this API key. Present on every response.
          schema:
            type: integer
externalDocs:
  description: Interactive documentation, product list and worked examples
  url: https://app.zelnum.com/docs/en
