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

# Get Email Check Result

> Poll an email check and read the answer for each product

<Note>
  **Availability:** Agency plan, on `https://app.gethuntd.com`. Other plans receive
  `403 ENDPOINT_NOT_ON_PLAN`. Polling is free but counts toward your API key's rate limit.
</Note>

<RequestExample>
  ```bash cURL theme={null}
  curl -X GET "https://app.gethuntd.com/api/v1/public/verify/8f4c2a90-6c1e-4b58-9a2f-1d3e5b7c9011" \
    -H "X-API-Key: hntd_abc12345_yoursecretkey"
  ```

  ```javascript JavaScript theme={null}
  const jobId = '8f4c2a90-6c1e-4b58-9a2f-1d3e5b7c9011';

  const response = await fetch(
    `https://app.gethuntd.com/api/v1/public/verify/${jobId}`,
    { headers: { 'X-API-Key': process.env.HUNTD_API_KEY } }
  );

  const job = await response.json();
  if (job.status === 'completed') {
    for (const [source, answer] of Object.entries(job.sources)) {
      console.log(source, answer.is_user); // true, false, or null if that source failed
    }
  }
  ```

  ```python Python theme={null}
  import requests
  import os

  job_id = '8f4c2a90-6c1e-4b58-9a2f-1d3e5b7c9011'

  response = requests.get(
      f'https://app.gethuntd.com/api/v1/public/verify/{job_id}',
      headers={'X-API-Key': os.environ['HUNTD_API_KEY']}
  )

  job = response.json()
  if job['status'] == 'completed':
      for source, answer in job['sources'].items():
          print(source, answer['is_user'])  # True, False, or None if that source failed
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "success": true,
    "job_id": "8f4c2a90-6c1e-4b58-9a2f-1d3e5b7c9011",
    "email": "dana@acme.com",
    "status": "completed",
    "sources": {
      "gong":   { "status": "done", "is_user": true,  "checked_at": "2026-09-12T08:14:22.000Z", "cached": true  },
      "extend": { "status": "done", "is_user": false, "checked_at": "2026-09-15T09:04:51.000Z", "cached": false }
    }
  }
  ```

  ```json 200 (running) theme={null}
  {
    "success": true,
    "job_id": "8f4c2a90-6c1e-4b58-9a2f-1d3e5b7c9011",
    "email": "dana@acme.com",
    "status": "running",
    "sources": {
      "gong":   { "status": "done",    "is_user": null, "checked_at": null, "cached": true  },
      "extend": { "status": "running", "is_user": null, "checked_at": null, "cached": false }
    }
  }
  ```

  ```json 401 theme={null}
  {
    "success": false,
    "error": {
      "code": "INVALID_API_KEY",
      "message": "API key is missing, invalid, or has been revoked."
    }
  }
  ```

  ```json 404 theme={null}
  {
    "success": false,
    "error": {
      "code": "JOB_NOT_FOUND",
      "message": "No such verification job."
    }
  }
  ```

  ```json 429 theme={null}
  {
    "success": false,
    "error": {
      "code": "RATE_LIMIT_EXCEEDED",
      "message": "Rate limit exceeded — 60 requests per minute. Retry in 23s."
    }
  }
  ```
</ResponseExample>

## Overview

Retrieve the progress of an email check and, once it's finished, the answer for each source.
Returns `200` for any job that exists: read `status` to know whether it's done.

## Reading the Result

For each source, `is_user` is the answer:

| `is_user` | Meaning | What to do |
| - | - | - |
| `true` | This person uses the product | Use the answer |
| `false` | We checked, and this person does not use the product | Use the answer. It's a real "no", not a missing value. |
| `null` | No answer: that source's `status` is `failed` | Submit again if you still need it |

<Warning>
  **Only read `is_user` once the job's `status` is `completed`.** Until then it's `null` for every
  source, even ones that have already finished. All answers are released together.
</Warning>

`checked_at` is when the check actually ran, so a cached answer (`cached: true`) keeps its original
date and can be older than the job.

## Job Status

| Status | Description |
| - | - |
| `queued` | Nothing has started yet |
| `running` | At least one source is being checked |
| `completed` | Every source is `done` or `failed`. Answers are filled in now. |

## Polling Strategy

1. Wait until the `poll_after` time from the submit response (skip this if it was `null`)
2. Poll every 5 seconds, backing off toward 60 seconds
3. Stop when `status` is `completed`

<CodeGroup>
  ```javascript JavaScript theme={null}
  const BASE = 'https://app.gethuntd.com/api/v1/public';
  const HEADERS = { 'X-API-Key': process.env.HUNTD_API_KEY };
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

  async function waitForResult(job) {
    // job is the response from Submit Email Check
    if (job.poll_after) {
      await sleep(Math.max(0, new Date(job.poll_after) - Date.now()));
    }

    for (let delay = 5_000; ; delay = Math.min(delay * 2, 60_000)) {
      const res = await fetch(`${BASE}/verify/${job.job_id}`, { headers: HEADERS });
      if (res.status === 429) {
        await sleep(Number(res.headers.get('Retry-After') ?? 60) * 1000);
        continue;
      }
      const view = await res.json();
      if (!view.success) throw new Error(`${view.error.code}: ${view.error.message}`);
      if (view.status === 'completed') return view.sources;
      await sleep(delay);
    }
  }
  ```

  ```python Python theme={null}
  import os
  import time
  from datetime import datetime, timezone

  import requests

  BASE = 'https://app.gethuntd.com/api/v1/public'
  HEADERS = {'X-API-Key': os.environ['HUNTD_API_KEY']}


  def wait_for_result(job):
      # job is the response from Submit Email Check
      if job['poll_after']:
          start = datetime.fromisoformat(job['poll_after'].replace('Z', '+00:00'))
          time.sleep(max(0, (start - datetime.now(timezone.utc)).total_seconds()))

      delay = 5
      while True:
          res = requests.get(f"{BASE}/verify/{job['job_id']}", headers=HEADERS)
          if res.status_code == 429:
              time.sleep(int(res.headers.get('Retry-After', 60)))
              continue
          view = res.json()
          if not view['success']:
              raise RuntimeError(f"{view['error']['code']}: {view['error']['message']}")
          if view['status'] == 'completed':
              return view['sources']
          time.sleep(delay)
          delay = min(delay * 2, 60)
  ```
</CodeGroup>

<Tip>
  A job queued for hours usually isn't stuck: checks only run Monday to Friday, 09:00 to 18:00 in
  your account's timezone. There is no webhook for this endpoint, so for long waits save `job_id`
  and check back from a scheduled job.
</Tip>

## Errors

| HTTP | Error Code | Description |
| - | - | - |
| 401 | `INVALID_API_KEY` | API key is missing, invalid, or has been revoked |
| 403 | `ENDPOINT_NOT_ON_PLAN` | Your plan doesn't include email checks |
| 403 | `SUBSCRIPTION_INACTIVE` | Your workspace has no active subscription. Contact your account manager. |
| 404 | `JOB_NOT_FOUND` | No such job for your organization. A job belonging to another organization also returns 404. |
| 429 | `RATE_LIMIT_EXCEEDED` | Over your API key's per-minute limit, which includes polls. Wait `Retry-After` seconds. |
| 500 | `INTERNAL_ERROR` | Something broke on our side. Safe to retry. |
| 503 | `ORG_KIND_UNAVAILABLE` | Your plan briefly couldn't be confirmed. Retry. |


## OpenAPI

````yaml api-reference/public-api.json GET /api/v1/public/verify/{job_id}
openapi: 3.0.3
info:
  title: Huntd Public API
  version: 1.0.0
  description: >-
    Read what Huntd tracks for your organization (enterprise plan), or check one
    email address against the products you track (Agency plan).
  contact:
    name: Huntd Support
    email: support@gethuntd.com
servers:
  - url: https://app.gethuntd.com
    description: Public API
security:
  - ApiKeyAuth: []
tags:
  - name: Public API
  - name: Email Check
paths:
  /api/v1/public/verify/{job_id}:
    get:
      tags:
        - Email Check
      summary: Get Email Check Result
      description: >-
        Poll an email check. Answers appear for every source at once, when the
        job's status is completed. Agency plan only.
      operationId: getEmailCheckResult
      parameters:
        - name: job_id
          in: path
          required: true
          description: The job ID returned by Submit Email Check
          schema:
            type: string
            format: uuid
            example: 8f4c2a90-6c1e-4b58-9a2f-1d3e5b7c9011
      responses:
        '200':
          description: The job's progress, and its answers once completed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailCheckResultResponse'
              examples:
                completed:
                  summary: Finished
                  value:
                    success: true
                    job_id: 8f4c2a90-6c1e-4b58-9a2f-1d3e5b7c9011
                    email: dana@acme.com
                    status: completed
                    sources:
                      gong:
                        status: done
                        is_user: true
                        checked_at: '2026-09-12T08:14:22.000Z'
                        cached: true
                      extend:
                        status: done
                        is_user: false
                        checked_at: '2026-09-15T09:04:51.000Z'
                        cached: false
                running:
                  summary: Still running
                  value:
                    success: true
                    job_id: 8f4c2a90-6c1e-4b58-9a2f-1d3e5b7c9011
                    email: dana@acme.com
                    status: running
                    sources:
                      gong:
                        status: done
                        is_user: null
                        checked_at: null
                        cached: true
                      extend:
                        status: running
                        is_user: null
                        checked_at: null
                        cached: false
        '401':
          description: Invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
              example:
                success: false
                error:
                  code: INVALID_API_KEY
                  message: API key is missing, invalid, or has been revoked.
        '403':
          description: Plan does not include email checks, or no active subscription
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
              examples:
                notOnPlan:
                  summary: Not available on your plan
                  value:
                    success: false
                    error:
                      code: ENDPOINT_NOT_ON_PLAN
                      message: This endpoint is not available on your plan.
                subscriptionInactive:
                  summary: Workspace has no active subscription
                  value:
                    success: false
                    error:
                      code: SUBSCRIPTION_INACTIVE
                      message: >-
                        This workspace doesn't have an active subscription.
                        Please contact your account manager to restore access.
        '404':
          description: No such job for your organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
              example:
                success: false
                error:
                  code: JOB_NOT_FOUND
                  message: No such verification job.
        '429':
          description: Over the API key's per-minute limit (polls count)
          headers:
            Retry-After:
              description: Seconds to wait before the next request.
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
              example:
                success: false
                error:
                  code: RATE_LIMIT_EXCEEDED
                  message: Rate limit exceeded — 60 requests per minute. Retry in 23s.
        '500':
          description: Internal error. Safe to retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
              example:
                success: false
                error:
                  code: INTERNAL_ERROR
                  message: Internal server error.
        '503':
          description: Plan briefly could not be confirmed. Retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
              example:
                success: false
                error:
                  code: ORG_KIND_UNAVAILABLE
                  message: >-
                    Your organization's plan could not be checked. Please retry
                    in a moment.
components:
  schemas:
    EmailCheckResultResponse:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - true
        job_id:
          type: string
          format: uuid
          example: 8f4c2a90-6c1e-4b58-9a2f-1d3e5b7c9011
        email:
          type: string
          example: dana@acme.com
        status:
          type: string
          enum:
            - queued
            - running
            - completed
          description: >-
            `completed` means every source is done or failed, and the answers
            are filled in.
        sources:
          type: object
          description: One entry per source slug.
          additionalProperties:
            $ref: '#/components/schemas/EmailCheckSourceResult'
    ApiError:
      type: object
      description: Every error uses this envelope. Branch on `error.code`, not the message.
      properties:
        success:
          type: boolean
          enum:
            - false
        error:
          type: object
          properties:
            code:
              type: string
              enum:
                - INVALID_PARAMETER
                - INVALID_BODY
                - INVALID_API_KEY
                - ENDPOINT_NOT_ON_PLAN
                - SOURCE_NOT_ALLOWED
                - SUBSCRIPTION_INACTIVE
                - JOB_NOT_FOUND
                - SOURCE_NOT_VERIFIABLE
                - SOURCE_UNAVAILABLE
                - RATE_LIMITED
                - RATE_LIMIT_EXCEEDED
                - QUOTA_EXCEEDED
                - STREAM_FAILED
                - INTERNAL_ERROR
                - ORG_KIND_UNAVAILABLE
            message:
              type: string
              description: Human-readable. May change.
    EmailCheckSourceResult:
      type: object
      description: Progress and answer for one source
      properties:
        status:
          type: string
          enum:
            - queued
            - running
            - done
            - failed
          description: >-
            `queued`: waiting for working hours or its turn. `running`: being
            checked. `done`: answered. `failed`: no answer is coming on this
            job.
        is_user:
          type: boolean
          nullable: true
          description: >-
            `true`: uses the product. `false`: checked, doesn't use it. `null`
            until the job is completed, and on a failed source.
        checked_at:
          type: string
          format: date-time
          nullable: true
          description: >-
            When the check actually ran. A cached answer keeps its original
            date.
        cached:
          type: boolean
          description: >-
            `true` when the answer came from a previous check rather than a new
            one.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: >-
        API key in format: hntd_{id}_{secret}. Create one in Settings → API &
        webhooks. `Authorization: Bearer` is also accepted.

````

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