> ## 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.

# List people

> Tracked people, paginated and date-filterable

```
GET https://app.gethuntd.com/api/v1/public/people
```

Returns the people Huntd tracks for your organization. Supports
[pagination](/public-api/conventions#pagination),
[date filtering](/public-api/conventions#date-filtering) and
[`source` / `is_user` filtering](/public-api/conventions#filtering).

## Request

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://app.gethuntd.com/api/v1/public/people?limit=100&is_user=true" \
    -H "X-API-Key: hntd_abc12345_yoursecretkey"
  ```

  ```javascript JavaScript theme={null}
  const params = new URLSearchParams({ limit: '100', is_user: 'true' });
  const res = await fetch(
    `https://app.gethuntd.com/api/v1/public/people?${params}`,
    { headers: { 'X-API-Key': process.env.HUNTD_API_KEY } }
  );
  const body = await res.json();
  ```

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

  body = requests.get(
      'https://app.gethuntd.com/api/v1/public/people',
      params={'limit': 100, 'is_user': 'true'},
      headers={'X-API-Key': os.environ['HUNTD_API_KEY']}
  ).json()
  ```
</CodeGroup>

## Response

```json theme={null}
{
  "success": true,
  "pagination": { "page": 1, "limit": 100, "total": 842, "totalPages": 9 },
  "data": [
    {
      "id": "62381c7cc5fc496eff56fd60",
      "firstName": "Ada",
      "lastName": "Lovelace",
      "email": "ada@acme.com",
      "jobTitle": "VP Engineering",
      "seniority": "VP",
      "department": "Engineering",
      "jobFunction": "Engineering",
      "company": "Acme",
      "companyDomain": "acme.com",
      "companySize": 420,
      "industry": "software,information technology & services",
      "location": "San Francisco",
      "country": "United States",
      "linkedinUrl": "https://linkedin.com/in/example",
      "sources": ["devin"],
      "discoveredAt": "2026-07-31T17:28:28.725Z"
    }
  ]
}
```

## Fields

<ResponseField name="id" type="string">
  Stable identifier. Use it to deduplicate across pages and across runs.
</ResponseField>

<ResponseField name="firstName" type="string" />

<ResponseField name="lastName" type="string" />

<ResponseField name="email" type="string" />

<ResponseField name="jobTitle" type="string">
  The person's title as we observed it.
</ResponseField>

<ResponseField name="seniority" type="string">
  Derived from job title. May be `null`.
</ResponseField>

<ResponseField name="department" type="string">
  Derived from job title. May be `null`.
</ResponseField>

<ResponseField name="jobFunction" type="string">
  Derived from job title. May be `null`.
</ResponseField>

<ResponseField name="company" type="string">
  Company display name.
</ResponseField>

<ResponseField name="companyDomain" type="string">
  **Normalized** domain. Joins directly to `domain` on [`/companies`](/public-api/companies).
</ResponseField>

<ResponseField name="companySize" type="integer">
  Employee count.
</ResponseField>

<ResponseField name="industry" type="string">
  Comma-separated industry labels.
</ResponseField>

<ResponseField name="location" type="string" />

<ResponseField name="country" type="string" />

<ResponseField name="linkedinUrl" type="string" />

<ResponseField name="sources" type="array">
  Slugs this person is a **confirmed user** of. May be empty — see the warning below.
</ResponseField>

<ResponseField name="discoveredAt" type="string">
  UTC timestamp. Always agrees with the date filter, which makes it safe as an incremental-sync
  cursor.
</ResponseField>

<Warning>
  An empty `sources` array means we **checked this person and found no user account** — not that
  they are untracked. Add `is_user=true` if you only want confirmed users.
</Warning>

## Incremental sync recipe

`discoveredAt` always agrees with the date filter, so a daily job can pull only what changed.

<CodeGroup>
  ```bash cURL theme={null}
  # Everything checked today and yesterday, confirmed users only
  curl "https://app.gethuntd.com/api/v1/public/people?discovered_days=2&is_user=true&limit=1000" \
    -H "X-API-Key: hntd_abc12345_yoursecretkey"
  ```

  ```javascript JavaScript theme={null}
  // Run daily. Overlap by one day so nothing is missed at the boundary.
  const params = new URLSearchParams({
    discovered_days: '2',
    is_user: 'true',
    limit: '1000'
  });

  const res = await fetch(
    `https://app.gethuntd.com/api/v1/public/people?${params}`,
    { headers: { 'X-API-Key': process.env.HUNTD_API_KEY } }
  );
  const body = await res.json();

  // `id` is stable — upsert on it so the overlap day does not create duplicates.
  for (const person of body.data) {
    await db.people.upsert({ where: { id: person.id }, data: person });
  }
  ```

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

  body = requests.get(
      'https://app.gethuntd.com/api/v1/public/people',
      params={'discovered_days': 2, 'is_user': 'true', 'limit': 1000},
      headers={'X-API-Key': os.environ['HUNTD_API_KEY']}
  ).json()

  # `id` is stable — upsert on it so the overlap day does not create duplicates.
  for person in body['data']:
      db.upsert_person(person['id'], person)
  ```
</CodeGroup>

<Tip>
  Overlap your window by a day (`discovered_days=2` for a daily job) and upsert on `id`. That
  costs nothing and protects you against a run that starts late or fails midway.
</Tip>

For a first full backfill, use [`/people/stream`](/public-api/streaming) instead of paging.
