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

# Conventions

> Envelope, pagination, date filtering, rate limits and errors

Everything on this page applies to every Public API endpoint.

## Response envelope

Successful responses carry `success: true`. List endpoints add `pagination` and `data`.

```json theme={null}
{
  "success": true,
  "pagination": { "page": 1, "limit": 100, "total": 842, "totalPages": 9 },
  "data": []
}
```

Errors carry `success: false`, matching the Company Lookup API's shape:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "INVALID_PARAMETER",
    "message": "`limit` may not exceed 1000."
  }
}
```

## Pagination

<ParamField query="page" type="integer" default="1">
  1-indexed page number. Applies to `/people` and `/companies`.
</ParamField>

<ParamField query="limit" type="integer" default="100">
  Results per page. Maximum **1000** — a higher value returns `400 INVALID_PARAMETER` rather
  than being silently clamped.
</ParamField>

<ParamField query="skip_count" type="integer">
  Set to `1` to skip the count query. `total` and `totalPages` return `null`, which is faster on
  large result sets.
</ParamField>

### Walking every page

<CodeGroup>
  ```bash cURL theme={null}
  # Page through until fewer than `limit` rows come back
  curl "https://app.gethuntd.com/api/v1/public/people?page=1&limit=1000" \
    -H "X-API-Key: hntd_abc12345_yoursecretkey"
  ```

  ```javascript JavaScript theme={null}
  const key = process.env.HUNTD_API_KEY;
  const base = 'https://app.gethuntd.com/api/v1/public/people';
  const all = [];

  for (let page = 1; ; page++) {
    const res = await fetch(`${base}?page=${page}&limit=1000`, {
      headers: { 'X-API-Key': key }
    });
    const body = await res.json();
    if (!body.success) throw new Error(body.error.code);

    all.push(...body.data);
    if (page >= body.pagination.totalPages) break;
  }
  ```

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

  key = os.environ['HUNTD_API_KEY']
  base = 'https://app.gethuntd.com/api/v1/public/people'
  all_people = []
  page = 1

  while True:
      body = requests.get(
          base,
          params={'page': page, 'limit': 1000},
          headers={'X-API-Key': key}
      ).json()
      if not body['success']:
          raise RuntimeError(body['error']['code'])

      all_people.extend(body['data'])
      if page >= body['pagination']['totalPages']:
          break
      page += 1
  ```
</CodeGroup>

<Tip>
  For a full backfill, prefer [streaming](/public-api/streaming) — one request instead of dozens,
  and it cannot miss rows the way offset paging can when data shifts mid-walk.
</Tip>

## Date filtering

All dates are **UTC**.

<ParamField query="discovered_days" type="integer">
  Last N UTC days, **including today**. Maximum `366`.
</ParamField>

<ParamField query="discovered_from" type="string">
  Inclusive UTC day in `YYYY-MM-DD` format.
</ParamField>

<ParamField query="discovered_to" type="string">
  Inclusive UTC day in `YYYY-MM-DD` format.
</ParamField>

Use either `discovered_days` **or** the `discovered_from`/`discovered_to` pair — never both, which
returns `400`. Either bound may be given on its own.

<Warning>
  **"Discovered" means when we last *checked* a person or company against a source — not when
  they were confirmed as a user.**

  Someone we checked yesterday who turned out *not* to be a user still matches
  `discovered_days=2`, and their `sources` array will be empty. Add `is_user=true` when you want
  confirmed users only.
</Warning>

## Filtering

<ParamField query="source" type="string">
  A source slug from [`/sources`](/public-api/sources).
</ParamField>

<ParamField query="is_user" type="string" default="all">
  `true` returns confirmed users only. `false` returns people we checked who are **not** users.
  Omit for both.
</ParamField>

```bash theme={null}
# People confirmed as Devin users in the last 7 days
curl "https://app.gethuntd.com/api/v1/public/people?source=devin&is_user=true&discovered_days=7" \
  -H "X-API-Key: hntd_abc12345_yoursecretkey"
```

## Rate limits

**60 requests per minute per key.** Every response carries:

| Header                  | Description                          |
| ----------------------- | ------------------------------------ |
| `X-RateLimit-Limit`     | Requests allowed per window          |
| `X-RateLimit-Remaining` | Requests left in the current window  |
| `X-RateLimit-Reset`     | When the window resets, Unix seconds |

A `429` response adds `Retry-After`.

<Note>
  A streaming request counts as **one** unit against this limit no matter how long it runs.
</Note>

## Error codes

| Status | Code                  | Meaning                                                     |
| ------ | --------------------- | ----------------------------------------------------------- |
| 400    | `INVALID_PARAMETER`   | Malformed or out-of-range query parameter                   |
| 401    | `INVALID_API_KEY`     | Key missing, malformed, unknown, or revoked                 |
| 403    | `SOURCE_NOT_ALLOWED`  | Your organization is not entitled to the requested `source` |
| 423    | `SOURCE_UNAVAILABLE`  | Source is temporarily under maintenance                     |
| 429    | `RATE_LIMIT_EXCEEDED` | Over 60/min — see `Retry-After`                             |
| 500    | `INTERNAL_ERROR`      | Our problem                                                 |

Always branch on `error.code` rather than the human-readable `error.message`, which may change.
