# API Verification Workflow

This guide is the complete reference for the ApexVerify batch verification lifecycle. It covers every API call, every parameter, every response shape — and the exact order of operations from uploading a file to downloading your results.

<Note>
  **Prerequisites:** A valid API key (see [Your First API Key](/quickstart/first-api-key)) and a `.txt` file containing one email or phone number per line.

  **Base URL:** `https://api.apexverify.com`\
  **Authentication:** All requests require the `X-Api-Key: YOUR_API_KEY` header.
</Note>

***

## Lifecycle overview

The batch verification workflow has **7 steps**. Each step maps to a specific API endpoint:

| Step | Action               | Method  | Endpoint                   |
| :--- | :------------------- | :------ | :------------------------- |
| 1    | Upload file          | `POST`  | `/v1/batch`                |
| 2    | Configure parameters | `PUT`   | `/v1/batch/{uuid}`         |
| 3    | Trigger parsing      | `PATCH` | `/v1/batch/{uuid}`         |
| 4    | Check readiness      | `GET`   | `/v1/batch/{uuid}/details` |
| 5    | Launch verification  | `POST`  | `/v1/batch/{uuid}`         |
| 6    | Poll for completion  | `GET`   | `/v1/batch/{uuid}`         |
| 7    | Export results       | `GET`   | `/v1/batch/{uuid}/export`  |

***

## Step 1: Upload your file

**Endpoint:** `POST /v1/batch`\
**Content-Type:** `multipart/form-data`

Upload a `.txt` file with one email or phone number per line. The API creates a batch record and returns a `batch_uuid` that identifies this batch for all subsequent operations.

### Request fields

| Field            | Type               | Required | Description                                    |
| :--------------- | :----------------- | :------- | :--------------------------------------------- |
| `file`           | binary             | **Yes**  | `.txt` file, UTF-8 encoded, one entry per line |
| `name`           | string             | **Yes**  | Batch display name (1–255 characters)          |
| `type`           | `email` \| `phone` | **Yes**  | Verification type                              |
| `target_country` | ISO2 string        | **Yes**  | Country code (e.g. `US`, `SG`, `GB`)           |
| `description`    | string             | No       | Optional description (max 2,048 chars)         |

### Example

```bash
curl -X POST https://api.apexverify.com/v1/batch \
  -H "X-Api-Key: YOUR_API_KEY" \
  -F "file=@contacts.txt" \
  -F "name=Q2 Lead List" \
  -F "description=Imported from HubSpot export — April 2026" \
  -F "type=email" \
  -F "target_country=US"
```

### Responses

<Tabs>
  <Tab title="200 Success">
    ```json
    {
      "status": "ok",
      "batch_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "created_at": "2026-04-06T10:00:00Z"
    }
    ```

    Save the `batch_uuid`. You will pass it to every subsequent request.
  </Tab>

  <Tab title="parse_error">
    ```json
    { "status": "parse_error" }
    ```

    The file was rejected. Ensure it is a valid UTF-8 `.txt` file with one entry per line — no commas, no headers, no BOM marker.
  </Tab>

  <Tab title="error">
    ```json
    { "status": "error" }
    ```

    An unexpected server error occurred. Retry the request or contact support if it persists.
  </Tab>
</Tabs>

<Note>
  Save your `batch_uuid` — it is the key identifier for this batch in all subsequent calls.
</Note>

***

## Step 2: Configure verification parameters

**Endpoint:** `PUT /v1/batch/{uuid}`\
**Content-Type:** `application/json`

Set the verification parameters for your batch. You can call this endpoint multiple times — parameters are updated each time. Only `type` and `target_country` are required.

### Parameters reference

| Field                       | Type               | Required | Default | Description                            |
| :-------------------------- | :----------------- | :------- | :------ | :------------------------------------- |
| `type`                      | `email` \| `phone` | **Yes**  | —       | Verification type                      |
| `target_country`            | ISO2 string        | **Yes**  | —       | Target country (e.g. `US`, `GB`, `SG`) |
| `target_audience`           | integer 1–23       | No       | `null`  | Audience role code                     |
| `target_market_industry`    | integer 1–115      | No       | `null`  | Industry vertical code                 |
| `target_objective`          | integer 1–16       | No       | `null`  | Campaign objective code                |
| `use_account_cache`         | boolean            | No       | `true`  | Reuse your own previous results        |
| `max_account_cache_backoff` | integer 1–180      | No       | `30`    | Days lookback for account cache        |
| `use_global_cache`          | boolean            | No       | `true`  | Reuse anonymized global results        |
| `max_global_cache_backoff`  | integer 1–180      | No       | `30`    | Days lookback for global cache         |
| `remove_duplicate`          | boolean            | No       | `true`  | Remove duplicate rows during prep      |
| `remove_wrong_email_format` | boolean            | No       | `true`  | Remove malformed email rows            |
| `remove_wrong_phone_format` | boolean            | No       | `true`  | Remove malformed phone rows            |

<AccordionGroup>
  <Accordion title="Audience codes (target_audience)">
    | Code | Audience                              |
    | :--- | :------------------------------------ |
    | 1    | Agency / Consultants                  |
    | 2    | Creators & Influencers                |
    | 3    | Customer Support & Success            |
    | 4    | Developers & Technical Leads          |
    | 5    | E-commerce & Retail Managers          |
    | 6    | Enterprise Decision-Makers            |
    | 7    | Finance & Operations Professionals    |
    | 8    | Founders & C-Level Executives         |
    | 9    | Freelancers & Solopreneurs            |
    | 10   | Government & Public Sector Employees  |
    | 11   | HR & Talent Acquisition               |
    | 12   | Healthcare Professionals              |
    | 13   | Hobbyists & Enthusiasts               |
    | 14   | Investors & VCs                       |
    | 15   | Job Seekers                           |
    | 16   | Legal Professionals                   |
    | 17   | Marketing & Sales Professionals       |
    | 18   | Non-Profit Leaders & Staff            |
    | 19   | Parents & Families                    |
    | 20   | Product & Project Managers            |
    | 21   | Real Estate Professionals             |
    | 22   | Small-to-Medium Business (SMB) Owners |
    | 23   | Students & Educators                  |
  </Accordion>

  <Accordion title="Objective codes (target_objective)">
    | Code | Objective                             |
    | :--- | :------------------------------------ |
    | 1    | App Installs / Downloads              |
    | 2    | Audience Building (Email/Community)   |
    | 3    | Brand Awareness & Recall              |
    | 4    | Customer Acquisition / Sales          |
    | 5    | Free Trial or Freemium Signups        |
    | 6    | Lead Generation (Top/Mid-Funnel)      |
    | 7    | Loyalty & Referral Program Growth     |
    | 8    | Market & Customer Research            |
    | 9    | New Market Entry                      |
    | 10   | Partner & Affiliate Recruitment       |
    | 11   | Product Engagement & Feature Adoption |
    | 12   | Sales-Ready Leads (Bottom-Funnel)     |
    | 13   | Upsell / Cross-sell Revenue           |
    | 14   | User Retention & Churn Reduction      |
    | 15   | Waitlist / Pre-order Signups          |
    | 16   | Website / App Traffic Acquisition     |
  </Accordion>

  <Accordion title="Cache settings explained">
    Both caches are enabled by default and provide a **50% credit refund** for cache-hit records:

    * **Account cache** — reuses results from your own previous batches. Fully private.
    * **Global cache** — reuses anonymized, aggregated results from across all accounts. No PII is ever shared. By enabling this, you also contribute anonymized results back to the global pool.

    The system always checks account cache first, then global cache.

    `max_account_cache_backoff` and `max_global_cache_backoff` control the lookback window in days (1–180). A shorter window gives fresher results; a longer window maximizes cache hits and lowers cost.
  </Accordion>
</AccordionGroup>

### Example

```bash
curl -X PUT https://api.apexverify.com/v1/batch/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "email",
    "target_country": "US",
    "target_audience": 17,
    "target_market_industry": 46,
    "target_objective": 6,
    "use_account_cache": true,
    "use_global_cache": true,
    "max_account_cache_backoff": 60,
    "max_global_cache_backoff": 30
  }'
```

### Response

```json
{ "message": "Success" }
```

<Tip>
  **Adjust and re-configure freely.** You can call `PUT` as many times as you need — parameters are not locked until verification is launched. After changing parameters, always re-trigger parsing (Step 3) to recalculate cost and content summary.
</Tip>

***

## Step 3: Trigger parsing and preparation

**Endpoint:** `PATCH /v1/batch/{uuid}`\
**Request body:** None

This triggers asynchronous content parsing. The server reads your uploaded file, validates each row, removes duplicates and malformed entries (per your configuration), and calculates the estimated verification cost.

### Example

```bash
curl -X PATCH https://api.apexverify.com/v1/batch/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -H "X-Api-Key: YOUR_API_KEY"
```

### Responses

<Tabs>
  <Tab title="ok">
    ```json
    { "status": "ok" }
    ```

    Parsing has been triggered. The batch transitions to `parsing_content` status. Poll `GET /v1/batch/{uuid}` until it reaches `ready_for_verification`.
  </Tab>

  <Tab title="parse_error">
    ```json
    { "status": "parse_error" }
    ```

    The file content could not be parsed. Re-check the file format — UTF-8, one entry per line, no headers.
  </Tab>

  <Tab title="error">
    ```json
    { "status": "error" }
    ```

    An unexpected error occurred. Retry or contact support.
  </Tab>
</Tabs>

### Re-parsing after parameter changes

<Note>
  **PATCH is idempotent and can be called multiple times.** If you update parameters via `PUT` after an initial `PATCH`, simply call `PATCH` again to re-parse with the new configuration. There is no limit on re-parses before the batch is launched. Each re-parse recalculates the cost and content summary.
</Note>

**Typical iterate-and-refine flow:**

```
PUT  → set parameters
PATCH → trigger parsing
         ↓ check GET /v1/batch/{uuid}/details
         ↓ cost too high? adjust cache settings
PUT  → update cache settings
PATCH → re-parse with new settings
         ↓ cost acceptable
POST → launch verification
```

### Parsing result fields

Once parsing is complete (status = `ready_for_verification`), the `found_content` field in `GET /v1/batch/{uuid}/details` shows:

<Tabs>
  <Tab title="Email parsing summary">
    | Field             | Type    | Description                                          |
    | :---------------- | :------ | :--------------------------------------------------- |
    | `valid_email`     | integer | Rows parsed as valid email format — will be verified |
    | `wrong_email`     | integer | Rows rejected as invalid format                      |
    | `duplicate_email` | integer | Rows removed as duplicates                           |

    Credits are charged for `valid_email` count only.
  </Tab>

  <Tab title="Phone parsing summary">
    | Field             | Type    | Description                                          |
    | :---------------- | :------ | :--------------------------------------------------- |
    | `valid_phone`     | integer | Rows parsed as valid phone format — will be verified |
    | `wrong_phone`     | integer | Rows rejected as invalid format                      |
    | `duplicate_phone` | integer | Rows removed as duplicates                           |
  </Tab>
</Tabs>

***

## Step 4: Check batch readiness

**Endpoint:** `GET /v1/batch/{uuid}/details`

Returns detailed batch information, including status, parameters, parsing summary, verification cost, and (after completion) statistics. The response shape is **discriminated by the `status` field**.

### Status discriminator

| `status` value           | Response schema                    | Meaning                      |
| :----------------------- | :--------------------------------- | :--------------------------- |
| `parsing_content`        | `BaseBatchDetailsModelResponse`    | Parsing in progress — wait   |
| `internal_error`         | `BaseBatchDetailsModelResponse`    | System error during parsing  |
| `ready_for_verification` | `PendingBatchDetailsModelResponse` | Ready to launch              |
| `verification_ongoing`   | `PendingBatchDetailsModelResponse` | Verification is running      |
| `verification_done`      | `FinalBatchDetailsModelResponse`   | Complete — results available |

### Example

```bash
curl -X GET https://api.apexverify.com/v1/batch/a1b2c3d4-e5f6-7890-abcd-ef1234567890/details \
  -H "X-Api-Key: YOUR_API_KEY"
```

### Response examples

<Tabs>
  <Tab title="Base (parsing / error)">
    Returned when `status` is `parsing_content` or `internal_error`:

    ```json
    {
      "batch_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "name": "Q2 Lead List",
      "description": "Imported from HubSpot export — April 2026",
      "status": "parsing_content",
      "created_at": "2026-04-06T10:00:00Z",
      "updated_at": "2026-04-06T10:00:05Z",
      "parameters": {
        "type": "email",
        "target_country": "US"
      }
    }
    ```
  </Tab>

  <Tab title="Pending (ready / ongoing)">
    Returned when `status` is `ready_for_verification` or `verification_ongoing`. Adds `found_content` and `found_cost`:

    ```json
    {
      "batch_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "name": "Q2 Lead List",
      "status": "ready_for_verification",
      "parameters": { "type": "email", "target_country": "US" },
      "found_content": {
        "valid_email": 9420,
        "wrong_email": 312,
        "duplicate_email": 268
      },
      "found_cost": {
        "total_email_cost": 9420,
        "net_email_cost": 7250.0,
        "email_cache_account_return": 850,
        "email_cache_global_return": 1320
      }
    }
    ```

    `net_email_cost` is the number of credits that will be charged after cache returns are applied.
  </Tab>

  <Tab title="Final (done)">
    Returned when `status` is `verification_done`. Adds `stats` with aggregate results:

    ```json
    {
      "batch_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "name": "Q2 Lead List",
      "status": "verification_done",
      "found_content": { "valid_email": 9420, "wrong_email": 312, "duplicate_email": 268 },
      "found_cost": { "total_email_cost": 9420, "net_email_cost": 7250.0, "email_cache_account_return": 850, "email_cache_global_return": 1320 },
      "stats": {
        "total_verification": 9420,
        "total_verification_valid": 7834,
        "total_verification_wrong": 891,
        "total_verification_unknown": 695,
        "total_verification_duplicate": 268,
        "total_verification_account_cache": 850,
        "total_verification_global_cache": 1320
      }
    }
    ```
  </Tab>
</Tabs>

<Warning>
  If `status` is `internal_error`, an unexpected error occurred during parsing. Contact support at **[contact@apexverify.com](mailto:contact@apexverify.com)** and include your `batch_uuid` so the team can investigate.
</Warning>

***

## Step 5: Launch verification

**Endpoint:** `POST /v1/batch/{uuid}`\
**Request body:** None

Starts the verification job. The batch must be in `ready_for_verification` status before calling this endpoint.

### Example

```bash
curl -X POST https://api.apexverify.com/v1/batch/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -H "X-Api-Key: YOUR_API_KEY"
```

### Responses

<Tabs>
  <Tab title="ok">
    ```json
    { "status": "ok" }
    ```

    Verification has started. Proceed to Step 6 to poll for completion.
  </Tab>

  <Tab title="not_ready_for_verification">
    ```json
    { "status": "not_ready_for_verification" }
    ```

    The batch is still parsing or has an error. Go back to Step 3 (PATCH) or check the details endpoint.
  </Tab>

  <Tab title="verification_already_ongoing">
    ```json
    { "status": "verification_already_ongoing" }
    ```

    Verification is already running — no action needed. Skip to Step 6 and poll.
  </Tab>

  <Tab title="verification_already_done">
    ```json
    { "status": "verification_already_done" }
    ```

    Verification has already completed. Skip to Step 7 and export.
  </Tab>

  <Tab title="error">
    ```json
    { "status": "error" }
    ```

    An unexpected error occurred. Contact support with your `batch_uuid`.
  </Tab>
</Tabs>

<Warning>
  **Insufficient credits?** If your account doesn't have enough credits to cover the `net_email_cost` or `net_phone_cost`, the API returns `402 Payment Required`. Top up your credits from the [Dashboard](https://app.apexverify.com) and retry.
</Warning>

***

## Step 6: Poll for completion

**Endpoint:** `GET /v1/batch/{uuid}`

This lightweight status endpoint returns the batch's current state. Poll it periodically until the status reaches `verification_done`.

### Example

```bash
curl -X GET https://api.apexverify.com/v1/batch/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -H "X-Api-Key: YOUR_API_KEY"
```

### Status values

| `status`                 | Meaning                                |
| :----------------------- | :------------------------------------- |
| `parsing_content`        | Batch is being prepared                |
| `ready_for_verification` | Waiting to be launched                 |
| `verification_ongoing`   | Verification is running — keep polling |
| `verification_done`      | Complete — ready to export             |
| `internal_error`         | Error — contact support                |

### Polling loop example

```bash
BATCH_UUID="a1b2c3d4-e5f6-7890-abcd-ef1234567890"
API_KEY="YOUR_API_KEY"
DELAY=5

while true; do
  STATUS=$(curl -s -X GET "https://api.apexverify.com/v1/batch/$BATCH_UUID" \
    -H "X-Api-Key: $API_KEY" | jq -r '.status')

  echo "Status: $STATUS"

  if [ "$STATUS" = "verification_done" ]; then
    echo "Verification complete — ready to export."
    break
  elif [ "$STATUS" = "internal_error" ]; then
    echo "Error encountered. Contact support with batch_uuid: $BATCH_UUID"
    break
  fi

  sleep $DELAY
  DELAY=$((DELAY < 60 ? DELAY * 2 : 60))  # exponential backoff, cap at 60s
done
```

<Tip>
  Use **exponential backoff** when polling. Start with a 5-second interval and double it each cycle, capping at 60 seconds. This avoids unnecessary API calls and stays well within the 180 requests/minute rate limit. See [API Rate Limits](/api-reference/rate-limits) for details.
</Tip>

***

## Step 7: Export your results

**Endpoint:** `GET /v1/batch/{uuid}/export`

Once `verification_done`, export your results. Two formats are available: structured JSON or a formatted XLSX spreadsheet.

### Query parameters

| Parameter            | Type             | Required | Default | Description                             |
| :------------------- | :--------------- | :------- | :------ | :-------------------------------------- |
| `format`             | `xlsx` \| `json` | **Yes**  | —       | Output format                           |
| `include_details`    | boolean          | No       | `true`  | Include per-row result fields in JSON   |
| `include_statistics` | boolean          | No       | `true`  | Include aggregate stats section in JSON |

***

<Tabs>
  <Tab title="JSON export">
    Returns a JSON object keyed by the original email or phone value. Each key maps to a full result object.

    ### Request

    ```bash
    curl -X GET "https://api.apexverify.com/v1/batch/a1b2c3d4-e5f6-7890-abcd-ef1234567890/export?format=json&include_details=true&include_statistics=true" \
      -H "X-Api-Key: YOUR_API_KEY"
    ```

    ### Email result structure

    ```json
    {
      "alice@example.com": {
        "email": "alice@example.com",
        "valid": true,
        "wrong": false,
        "unknown": false,
        "quality": "good",
        "result": "ok",
        "is_syntax_error": false,
        "is_free": false,
        "is_role": false,
        "is_disposable": false,
        "is_catch_all": false
      },
      "info@company.org": {
        "email": "info@company.org",
        "valid": true,
        "wrong": false,
        "unknown": false,
        "quality": "risky",
        "result": "ok",
        "is_syntax_error": false,
        "is_free": false,
        "is_role": true,
        "is_disposable": false,
        "is_catch_all": false
      }
    }
    ```

    ### Email result field reference

    | Field             | Type    | Values                              | Description                             |
    | :---------------- | :------ | :---------------------------------- | :-------------------------------------- |
    | `valid`           | boolean | —                                   | Deliverable and safe to contact         |
    | `wrong`           | boolean | —                                   | Invalid or undeliverable                |
    | `unknown`         | boolean | —                                   | Inconclusive result                     |
    | `quality`         | string  | `good`, `bad`, `risky`, `unknown`   | Overall quality signal                  |
    | `result`          | string  | `ok`, `invalid`, `error`, `unknown` | SMTP-level result                       |
    | `is_syntax_error` | boolean | —                                   | Fails RFC 5321/5322 syntax validation   |
    | `is_free`         | boolean | —                                   | Free provider (Gmail, Yahoo, Outlook…)  |
    | `is_role`         | boolean | —                                   | Role address (info@, admin@, support@…) |
    | `is_disposable`   | boolean | —                                   | Known disposable/one-time inbox service |
    | `is_catch_all`    | boolean | —                                   | Domain accepts all addresses            |

    ### Phone result structure

    ```json
    {
      "+6591234567": {
        "phone": "+6591234567",
        "valid": true,
        "wrong": false,
        "unknown": false,
        "type": "mobile",
        "status": "live",
        "is_syntax_error": false,
        "is_ported": false,
        "current_network_mcc": "525",
        "current_network_mnc": "01",
        "operator": "Singtel"
      }
    }
    ```

    ### Phone result field reference

    | Field                 | Type         | Values                                                                                                                                           | Description                                   |
    | :-------------------- | :----------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------- |
    | `valid`               | boolean      | —                                                                                                                                                | Active and reachable number                   |
    | `wrong`               | boolean      | —                                                                                                                                                | Invalid number                                |
    | `unknown`             | boolean      | —                                                                                                                                                | Inconclusive result                           |
    | `type`                | string       | `mobile`, `landline`, `mobile_or_landline`, `voip`, `toll_free`, `premium`, `shared_cost`, `pager`, `voicemail_only`, `bad_format`, `unknown`    | Number classification                         |
    | `status`              | string       | `live`, `dead`, `absent_subscriber`, `no_teleservice_provisioned`, `not_available_network_only`, `no_coverage`, `not_applicable`, `inconclusive` | Live network status                           |
    | `is_syntax_error`     | boolean      | —                                                                                                                                                | Fails E.164 format validation                 |
    | `is_ported`           | boolean      | —                                                                                                                                                | Number has been ported to a different carrier |
    | `current_network_mcc` | string\|null | —                                                                                                                                                | Mobile Country Code of current network        |
    | `current_network_mnc` | string\|null | —                                                                                                                                                | Mobile Network Code of current network        |
    | `operator`            | string\|null | —                                                                                                                                                | Current network operator name                 |
  </Tab>

  <Tab title="XLSX export">
    Returns a binary Excel file matching the layout from the ApexVerify web dashboard. Use `-o` with curl to write the file to disk.

    ### Request

    ```bash
    curl -X GET "https://api.apexverify.com/v1/batch/a1b2c3d4-e5f6-7890-abcd-ef1234567890/export?format=xlsx" \
      -H "X-Api-Key: YOUR_API_KEY" \
      -o results.xlsx
    ```

    The response `Content-Type` is `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`.

    ### What's included in the XLSX

    The spreadsheet contains one row per verified record, with all result fields as columns — equivalent to the JSON export with `include_details=true`. A summary statistics sheet is included at the front.

    The XLSX format is ideal for:

    * Sharing results with non-technical stakeholders
    * Opening directly in Excel, Google Sheets, or LibreOffice Calc
    * Applying spreadsheet-based filtering and sorting
  </Tab>
</Tabs>

***

## Complete workflow: end-to-end example

```bash
API_KEY="YOUR_API_KEY"
BASE="https://api.apexverify.com"

# 1. Upload
UPLOAD=$(curl -s -X POST $BASE/v1/batch \
  -H "X-Api-Key: $API_KEY" \
  -F "file=@contacts.txt" \
  -F "name=April Campaign" \
  -F "type=email" \
  -F "target_country=US")
UUID=$(echo $UPLOAD | jq -r '.batch_uuid')
echo "Batch UUID: $UUID"

# 2. Configure parameters
curl -s -X PUT "$BASE/v1/batch/$UUID" \
  -H "X-Api-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type":"email","target_country":"US","use_account_cache":true,"use_global_cache":true}'

# 3. Trigger parsing
curl -s -X PATCH "$BASE/v1/batch/$UUID" \
  -H "X-Api-Key: $API_KEY"

# 4. Wait for ready_for_verification (simplified — use polling loop in production)
sleep 10

# 5. Launch verification
curl -s -X POST "$BASE/v1/batch/$UUID" \
  -H "X-Api-Key: $API_KEY"

# 6. Poll until done
DELAY=5
while true; do
  STATUS=$(curl -s "$BASE/v1/batch/$UUID" -H "X-Api-Key: $API_KEY" | jq -r '.status')
  [ "$STATUS" = "verification_done" ] && break
  sleep $DELAY
  DELAY=$((DELAY < 60 ? DELAY * 2 : 60))
done

# 7. Export JSON results
curl -s "$BASE/v1/batch/$UUID/export?format=json" \
  -H "X-Api-Key: $API_KEY" > results.json

# OR export XLSX
curl -s "$BASE/v1/batch/$UUID/export?format=xlsx" \
  -H "X-Api-Key: $API_KEY" \
  -o results.xlsx
```

***

## What's Next?

<CardGroup cols={2}>
  <Card title="List Verification" icon="fa-regular fa-list-check" href="/tutorials/list-verification">
    Best practices for preparing and verifying large contact lists — file format, parsing results, and result field interpretation.
  </Card>

  <Card title="Dataset Verification" icon="fa-regular fa-database" href="/tutorials/dataset-verification">
    Targeting parameters in detail — audience roles, industry codes, campaign objectives, and cache configuration.
  </Card>
</CardGroup>
