BillionVerify File Processing API

Batch file upload and processing endpoints

Operations 4

POST /v1/verify/file Upload file for verification #
GET /v1/verify/file/{job_id} Get file verification job status #
GET /v1/verify/file/{job_id}/results Download verification results #
POST /v1/jobs/{job_id}/resume Resume a paused file verification job #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/billionverify-com:billionverify-com-file-processing-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

billionverify-com-file-processing-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: BillionVerify File Processing API
  description: 'BillionVerify provides a powerful email verification API for validating email addresses in real-time.


    ## Authentication


    All API endpoints require authentication using an API key. Include your API key in the request header:


    ```

    BILLIONVERIFY-API-KEY: your_api_key_here

    ```


    The following header names are all accepted, in priority order:

    - `BV-API-KEY` *(recommended)*

    - `BILLIONVERIFY-API-KEY`

    - `Authorization: Bearer {api_key}`


    ## Rate Limits


    | Endpoint | Rate Limit |

    |----------|------------|

    | Single Verification (`/verify/single`) | 6,000 requests/minute |

    | Batch Verification (`/verify/bulk`) | 1,500 requests/minute |

    | File Upload (`/verify/file`) | 300 requests/minute |

    | Other endpoints | 200 requests/minute |


    ## Credits & Billing


    - **Unknown** status: 0 credits (we could not reach a deterministic conclusion, so you are not charged)

    - All other statuses (valid, invalid, risky, disposable, catchall, role): 1 credit each


    `invalid` is a deterministic result — we confirmed the address is not deliverable (SMTP 550, missing MX, mailbox not found, etc.) — and is just as valuable for list cleaning as `valid`, so it is billed the same.


    `risky` is likewise deterministic: we completed the full SMTP check *and* found the recipient domain listed by an external blocklist. See `risk_reasons` on the result object.


    > **Changed 2026-08-04.** `risky` previously meant "we could not finish verifying" (an internal capacity fallback) and was not billed. That case is now returned as `unknown`, unchanged in behaviour and still free. `risky` now exclusively means "deliverable, but the domain has a confirmed abuse history — do not send", is billed 1 credit, and is never retryable. If your integration treats `risky` as "retry later", update it.


    ## Base URL


    Production: `https://api.billionverify.com`'
  version: 1.0.0
  contact:
    name: BillionVerify Support
    email: support@billionverify.com
    url: https://billionverify.com
  license:
    name: Proprietary
    url: https://billionverify.com/terms
servers:
- url: https://api.billionverify.com
  description: Production server
security:
- ApiKeyAuth: []
tags:
- name: File Processing
  description: Batch file upload and processing endpoints
paths:
  /v1/verify/file:
    post:
      tags:
      - File Processing
      summary: Upload file for verification
      description: 'Upload a file containing email addresses for asynchronous batch verification.


        **Supported formats:**

        - CSV (.csv)

        - Excel (.xlsx)

        - Text (.txt) - one email per line


        **Limits:**

        - Maximum file size: 20 GB (aligned with the web uploader)

        - No application-enforced row-count limit


        **Large uploads:** small files are parsed synchronously and the response

        includes email counts. Files above the inline-parse threshold are accepted

        and prepared asynchronously: the response returns `status: preparing`

        without counts, and clients poll `status_url` for progress and counts.'
      operationId: uploadFileForVerification
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/FileUploadRequest'
      responses:
        '200':
          description: File uploaded successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileUploadApiResponse'
              example:
                success: true
                code: '0'
                message: Success
                data:
                  task_id: 7143874e-21c5-43c1-96f3-2b52ea41ae6a
                  status: pending
                  message: File uploaded successfully, processing started
                  file_name: emails.csv
                  file_size: 45678
                  estimated_count: 1000
                  unique_emails: 950
                  total_rows: 1000
                  email_column: email
                  status_url: /v1/verify/file/7143874e-21c5-43c1-96f3-2b52ea41ae6a
                  created_at: '2026-02-04T10:30:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCredits'
        '413':
          description: File too large
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/verify/file/{job_id}:
    get:
      tags:
      - File Processing
      summary: Get file verification job status
      description: 'Check the status and progress of a file verification job.

        Supports long-polling with the `timeout` parameter.

        File job statuses are exhaustively enumerated as

        `preparing`, `pending`, `running`, `processing`, `merging`, `paused`,

        `completed`, `completed_with_warning`, `failed`, and `cancelled`.'
      operationId: getFileJobStatus
      parameters:
      - name: job_id
        in: path
        required: true
        description: The job ID returned from file upload
        schema:
          type: string
          format: uuid
      - name: timeout
        in: query
        required: false
        description: Long-polling timeout in seconds (0-300). If set, the request will wait until job completes or timeout.
        schema:
          type: integer
          minimum: 0
          maximum: 300
          default: 0
      responses:
        '200':
          description: Job status retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileVerifyTaskStatusApiResponse'
              example:
                success: true
                code: '0'
                message: Success
                data:
                  task_id: 7143874e-21c5-43c1-96f3-2b52ea41ae6a
                  status: completed
                  progress: 100
                  email_progress: 100
                  chunk_progress: 100
                  progress_source: email
                  total_emails: 1000
                  processed_emails: 1000
                  valid_emails: 850
                  invalid_emails: 100
                  unknown_emails: 30
                  role_emails: 15
                  catchall_emails: 5
                  risky_emails: 0
                  disposable_emails: 0
                  credits_used: 970
                  runtime_hints:
                    has_live_execution: true
                    inflight_batches: 4
                    last_execution_fact_at: '2026-02-04T10:31:58Z'
                  download_url: https://api.billionverify.com/v1/verify/file/7143874e-21c5-43c1-96f3-2b52ea41ae6a/results
                  direct_download_url: https://download.example.com/results_7143874e.csv?signature=...
                  direct_download_expires_at: '2026-02-04T11:32:05Z'
                  result_download_available: true
                  result_download_expires_at: '2026-02-11T10:32:05Z'
                  result_download_unavailable_reason: ''
                  started_at: '2026-02-04T10:30:00Z'
                  completed_at: '2026-02-04T10:32:05Z'
                  can_pause: false
                  can_resume: false
                  can_restart: false
                  restart_attempts: 0
                  current_attempt: 1
                  deliverability_buckets:
                    strict_deliverable: 865
                    usable_with_catchall: 870
                    not_deliverable: 100
                    unknown: 30
                    anti_verified: 12
                    transient_failure: 4
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /v1/verify/file/{job_id}/results:
    get:
      tags:
      - File Processing
      summary: Download verification results
      description: 'Download the verification results for a completed job.


        **Without filters:** Returns redirect (HTTP 307) to the full result file.


        **With filters:** Returns a CSV containing only matching emails.


        Multiple filters can be combined (OR logic).'
      operationId: downloadFileResults
      parameters:
      - name: job_id
        in: path
        required: true
        description: The job ID
        schema:
          type: string
          format: uuid
      - name: valid
        in: query
        description: Include valid emails
        schema:
          type: boolean
      - name: invalid
        in: query
        description: Include invalid emails
        schema:
          type: boolean
      - name: catchall
        in: query
        description: Include catch-all emails
        schema:
          type: boolean
      - name: role
        in: query
        description: Include role emails
        schema:
          type: boolean
      - name: unknown
        in: query
        description: Include unknown emails
        schema:
          type: boolean
      - name: disposable
        in: query
        description: Include disposable emails
        schema:
          type: boolean
      - name: risky
        in: query
        description: 'Include risky emails (deliverable, but the domain has a confirmed abuse

          history — see `risk_reasons`). Most list-cleaning exports should leave this

          off, the same way they leave `disposable` off.

          '
        schema:
          type: boolean
      responses:
        '200':
          description: CSV file with filtered results
          content:
            text/csv:
              schema:
                type: string
                format: binary
        '307':
          description: Redirect to full result file (when no filters applied)
          headers:
            Location:
              description: URL to download the result file
              schema:
                type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /v1/jobs/{job_id}/resume:
    post:
      tags:
      - File Processing
      summary: Resume a paused file verification job
      description: 'Resume the current paused attempt of a file verification job.

        Resume does not create a new attempt. Repeated resume requests on the same

        already-resumed attempt are treated as idempotent no-ops and return the current status.'
      operationId: resumePausedJob
      parameters:
      - name: job_id
        in: path
        required: true
        description: The paused job ID
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Job resumed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileVerifyTaskStatusApiResponse'
              example:
                success: true
                code: '0'
                message: Success
                data:
                  task_id: 7143874e-21c5-43c1-96f3-2b52ea41ae6a
                  status: processing
                  progress: 66
                  total_emails: 1000
                  processed_emails: 660
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          description: Failed to update task state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    DeliverabilityBuckets:
      type: object
      description: "Q7 (2026-05-08) + §13 Finding 6 (2026-05-09): pre-aggregated business buckets\nso clients don't have to parse each row's status/reason. Returned on\n`/v1/verify/bulk` and `/v1/verify/file/{job_id}`.\n\nMath: `strict_deliverable + (usable_with_catchall - strict_deliverable) +\n      not_deliverable + unknown = total_emails`.\n\n`anti_verified + transient_failure ≤ unknown` (NOT equal — the residual\nunknown rows are smtp_blocked / google_dmarc_misalignment / non-anti-domain\nsmtp_unverifiable etc., which retrying does not improve).\n\n> **Changed 2026-08-04.** `risky` rows moved from the `unknown` bucket into\n> `not_deliverable`. They are no longer \"we could not tell\" — they are \"do not\n> send\", the same action as `disposable`, which already lives in that bucket.\n> The math above is unaffected.\n"
      properties:
        strict_deliverable:
          type: integer
          description: valid + role
        usable_with_catchall:
          type: integer
          description: strict_deliverable + catchall (broader reach if you accept catchall risk)
        not_deliverable:
          type: integer
          description: 'invalid + disposable + risky — every row you should drop from the list.

            `disposable` and `risky` addresses often *do* accept mail; this bucket means

            "do not send", not "cannot be delivered".

            '
        unknown:
          type: integer
          description: 'total unknown rows (super-set of anti_verified + transient_failure).

            Includes the SMTP capacity fallback

            (reason=mx_only_verification_semaphore_timeout), which was reported as

            `risky` before 2026-08-04.

            '
        anti_verified:
          type: integer
          description: 'unknown rows whose domain is in the anti-verification consumer list

            (Apple iCloud / Microsoft consumer / Proton / Tuta) AND reason=smtp_unverifiable.

            Retrying does NOT help — these providers refuse SMTP probing entirely.

            '
        transient_failure:
          type: integer
          description: 'unknown rows whose reason is in the retryable whitelist (smtp_rate_limited /

            rate_limited / smtp_timeout / dns_timeout / dns_lookup_error /

            capacity_deferred / context_cancelled / microsoft_api_error / microsoft_api_unavailable / unitedinternet_api_error / unitedinternet_api_unavailable / smtp_rate_check_failed / smtp_rate_wait_failed / internal_verifier_error / dns_resolution_failed / smtp_connection_failed / smtp_egress_unavailable / yahoo_api_rate_limited / smtp_eof_blocked /

            smtp_greylisted / mimecast_greylist /

            proofpoint_ad_lookup / policy_temp_fail / google_rate_limit /

            m365_ip_rep_block / yahoo_api_error / yahoo_api_unavailable /

            yahoo_api_eof / internal_verifier_error / smtp_550_unclassified /

            mx_only_verification_semaphore_timeout).

            Customer can `force_refresh=true` to retry.

            '
    FileVerifyTaskStatusApiResponse:
      type: object
      properties:
        success:
          type: boolean
        code:
          type: string
        message:
          type: string
        data:
          $ref: '#/components/schemas/FileVerifyTaskStatus'
        error:
          allOf:
          - $ref: '#/components/schemas/ErrorInfo'
    ErrorInfo:
      type: object
      properties:
        code:
          type: string
          description: Detailed error code
        message:
          type: string
          description: Detailed error message
        description:
          type:
          - string
          - 'null'
          description: Additional human-readable context (omitempty in Go)
        details:
          description: Additional structured context (any type; usually map or string; omitempty in Go).
    FileUploadApiResponse:
      type: object
      properties:
        success:
          type: boolean
        code:
          type: string
        message:
          type: string
        data:
          $ref: '#/components/schemas/FileVerifyTaskResponse'
        error:
          allOf:
          - $ref: '#/components/schemas/ErrorInfo'
    ErrorResponse:
      type: object
      description: 'Standard error response shape. Mirrors Go `response.StandardResponse`

        with Error populated. F23 (2026-05-09) replaced inline error object

        with `$ref ErrorInfo` so all typed wrappers + ErrorResponse share one

        canonical schema.

        '
      properties:
        success:
          type: boolean
          example: false
        code:
          type: string
          description: Top-level error code (mirrors Go StandardResponse.Code)
        message:
          type: string
          description: Top-level error message
        error:
          $ref: '#/components/schemas/ErrorInfo'
    FileVerifyTaskStatus:
      type: object
      description: 'Schema mirrors Go struct `service.FileVerifyTaskStatus`

        (`src/service/file_verify_service.go`). Reflect-based contract test

        guards that every JSON-serialized Go field appears here.

        '
      properties:
        capacity_wait:
          type: object
          description: Remaining addresses await capacity; next_eligible_at is a next-attempt time, not a completion estimate.
          properties:
            waiting_emails:
              type: integer
            next_eligible_at:
              type: string
              format: date-time
            waiting_since:
              type: string
              format: date-time
        task_id:
          type: string
        status:
          type: string
          description: preparing / pending / running / processing / merging / paused / completed / completed_with_warning / failed / cancelled
        stage:
          type:
          - string
          - 'null'
          enum:
          - verifying
          - retry_tail
          - generating_report
          - null
          description: Execution sub-stage while the job is processing/merging. "retry_tail" means the bulk of emails are done and the remainder are waiting on greylisting retry windows; "generating_report" means verification finished and the result CSV is being built.
        pending_retry_emails:
          type:
          - integer
          - 'null'
          description: Number of emails still waiting on retry windows. Only present when stage is "retry_tail".
        progress:
          type: integer
          description: 0-100
        email_progress:
          type:
          - integer
          - 'null'
        chunk_progress:
          type:
          - integer
          - 'null'
        progress_source:
          type:
          - string
          - 'null'
          description: email | chunk
        total_emails:
          type: integer
        processed_emails:
          type: integer
        valid_emails:
          type: integer
        invalid_emails:
          type: integer
        role_emails:
          type: integer
        catchall_emails:
          type: integer
        unknown_emails:
          type: integer
        risky_emails:
          type: integer
          description: 'Rows whose recipient domain is listed by an external blocklist. Counted in

            the `not_deliverable` bucket. Before 2026-08-04 this counter held internal

            capacity fallbacks instead, which are now reported under `unknown_emails`.

            '
        disposable_emails:
          type: integer
        disposable_charged_emails:
          type: integer
          description: disposable 命中里实际被扣费的 distinct 邮箱数 (SMTP opt-in 路径)。默认 0; 客户带 check_smtp:true 上传 file 且 disposable SMTP 出确定结果时, 这部分被计入 credits_used。详见 docs/disposable/disposable-billing-frontend.md。
        credits_used:
          type: integer
          format: int64
        cache_hit_not_charged:
          type:
          - integer
          - 'null'
          description: Number of rows served from user-cache without charging credits (terminal state only).
        started_at:
          type:
          - string
          - 'null'
          format: date-time
        completed_at:
          type:
          - string
          - 'null'
          format: date-time
        error_message:
          type:
          - string
          - 'null'
        pause_reason:
          type:
          - string
          - 'null'
        can_pause:
          type:
          - boolean
          - 'null'
        can_resume:
          type:
          - boolean
          - 'null'
        can_restart:
          type:
          - boolean
          - 'null'
        restart_attempts:
          type:
          - integer
          - 'null'
        current_attempt:
          type:
          - integer
          - 'null'
        download_url:
          type:
          - string
          - 'null'
          description: API download URL for results CSV (terminal completed state).
        direct_download_url:
          type:
          - string
          - 'null'
          description: Pre-signed direct-storage URL for results CSV (avoids API hop).
        direct_download_expires_at:
          type:
          - string
          - 'null'
          format: date-time
        result_download_available:
          type: boolean
          description: Whether result CSV/detail download is available for the completed task.
        result_download_expires_at:
          type:
          - string
          - 'null'
          format: date-time
          description: Reserved for a future configured deletion deadline; currently null.
        result_download_unavailable_reason:
          type:
          - string
          - 'null'
          description: Machine-readable reason when result download is unavailable.
        total_chunks:
          type:
          - integer
          - 'null'
        completed_chunks:
          type:
          - integer
          - 'null'
        failed_chunks:
          type:
          - integer
          - 'null'
        runtime_hints:
          allOf:
          - $ref: '#/components/schemas/FileVerifyTaskRuntimeHints'
        deliverability_buckets:
          allOf:
          - $ref: '#/components/schemas/DeliverabilityBuckets'
          description: 'Pre-aggregated deliverability buckets. Pointer in Go (omitempty) →

            absent for ephemeral / partial responses; populated on terminal

            states for file jobs and on every bulk response.

            '
        unique_emails:
          type:
          - integer
          - 'null'
          description: Distinct email count after dedup (used for billing).
        total_rows:
          type:
          - integer
          - 'null'
          description: Total input rows, excluding header.
    FileUploadRequest:
      type: object
      required:
      - file
      properties:
        file:
          type: string
          format: binary
          description: File containing email addresses (CSV, Excel, or TXT). Maximum size 20 GB; no row-count limit.
        check_smtp:
          type: boolean
          description: 'Optional SMTP override: when omitted, the server default applies (production: enabled). true explicitly enables SMTP; false explicitly disables SMTP for this request.'
        email_column:
          type: string
          description: Column name containing email addresses (auto-detected if not specified)
        preserve_original:
          type: boolean
          description: Keep original columns in result file
          default: true
    FileVerifyTaskResponse:
      type: object
      description: 'Returned from POST /v1/verify/file (file upload). Includes job_id and

        status_url customers poll to check verification progress.

        '
      properties:
        task_id:
          type: string
          description: Job ID; use this with GET /v1/verify/file/{job_id} to poll status.
        file_name:
          type: string
        file_size:
          type: integer
          format: int64
        status:
          type: string
          description: 'Initial job status. `pending` for synchronously parsed uploads;

            `preparing` for large uploads deferred to asynchronous preparation

            (counts arrive later via status polling).

            '
        message:
          type: string
          description: Human-readable status message.
        status_url:
          type: string
          description: Convenience URL clients can GET to poll task status (relative or absolute).
        created_at:
          type: string
          format: date-time
        estimated_count:
          type: integer
          description: Pre-parse estimated email count.
        unique_emails:
          type:
          - integer
          - 'null'
          description: Distinct email count after dedup. omitempty in Go.
        total_rows:
          type:
          - integer
          - 'null'
          description: Total file rows excluding header. omitempty in Go.
        email_column:
          type:
          - string
          - 'null'
          description: Detected email column name. omitempty in Go.
    FileVerifyTaskRuntimeHints:
      type: object
      description: 'Per-job runtime telemetry hints. Canonical Go struct

        `filejob.JobRuntimeHints` (`src/domain/filejob/read_model.go`);

        re-exported from `src/service` as `FileVerifyTaskRuntimeHints` via

        type alias (T3.12 path B).

        '
      properties:
        has_live_execution:
          type:
          - boolean
          - 'null'
          description: Whether worker is actively processing this job right now.
        inflight_batches:
          type:
          - integer
          - 'null'
          description: Number of batches currently in-flight on workers.
        last_execution_fact_at:
          type:
          - string
          - 'null'
          format: date-time
          description: Last time a worker reported live progress for this job.
  responses:
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            code: '4040'
            message: Not found
            error:
              code: JOB_NOT_FOUND
              message: The specified job was not found
    InsufficientCredits:
      description: Payment required - insufficient credits
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            code: '4020'
            message: Insufficient credits
            error:
              code: INSUFFICIENT_CREDITS
              message: Your account has insufficient credits. Please purchase more credits.
    BadRequest:
      description: Bad request - invalid parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            code: '4000'
            message: Invalid request
            error:
              code: INVALID_REQUEST
              message: Invalid email format
    Unauthorized:
      description: Unauthorized - invalid or missing API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            code: '4010'
            message: Unauthorized
            error:
              code: INVALID_API_KEY
              message: API key is invalid or missing
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: BV-API-KEY
      description: 'API key for authentication. The following header names are accepted, in priority order:

        - `BV-API-KEY` (recommended)

        - `BILLIONVERIFY-API-KEY`

        - `Authorization: Bearer {api_key}`

        '