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

# Search identification history

> Retrieve stored identifications by identifier with pagination and domain-scoped authentication.

Search identifications by Identification ID, user, device, visitor, session, cookie or IPv4 address. Results are scoped to the domain of your Private API Key.

```bash theme={null}
curl 'https://account.shieldlabs.ai/api/v1/history/identification_id/550e8400-e29b-41d4-a716-446655440000?limit=1' \
  -H "Authorization: Bearer $SHIELDLABS_API_KEY"
```

```json theme={null}
{
  "data": [{
    "identification_id": "550e8400-e29b-41d4-a716-446655440000",
    "device_id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
    "visitor_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "user_hid": "example-account-token",
    "domain": "example.com",
    "ip": "203.0.113.10",
    "country": "US",
    "os": "Windows",
    "browser": "Chrome",
    "device_type": "desktop",
    "connection_type": "vpn",
    "score": 30,
    "is_vpn": true,
    "created_at": "2026-10-13 10:00:00"
  }],
  "total": 1
}
```

## Authentication

**Authorization** `string` *required*

`Bearer PRIVATE_API_KEY`. Use the domain's Private API Key, not its browser Public Key or Management Secret Key.

An empty `data` array is valid: no matching result is stored yet, or none matches in this domain. For a new Identification ID, use a bounded retry or an SDK retrieval helper. Handle an unavailable result explicitly.

## Path parameters

**search\_type** `string` *required*

One of `identification_id`, `user_hid`, `device_id`, `visitor_id`, `ip`, `session_id` or `cookie_id`.

**value** `string` *required*

The identifier to match, URL-encoded once. `ip` accepts IPv4 addresses.

Unsupported search types return **400**. Empty values, `.` or `..`, slashes, backslashes, control characters and values longer than 1,024 bytes are rejected. Local IPs have no search type.

## Query parameters

**limit** `integer` (default: 20)

Number of rows to return, from 1 to 100.

**offset** `integer` (default: 0)

Non-negative number of matching rows to skip.

Invalid, empty or repeated pagination parameters return **400**, including a limit outside 1–100.

## Response

The endpoint returns a JSON object. The example shows a shortened History row.

**data** `object[]` *required*

Matching identifications, newest first. An empty array is valid. See [History row fields](https://docs.shieldlabs.ai/api/models.md#history-row).

**total** `integer` *required*

Total matches for this identifier and domain, before pagination.

History and webhooks have different field names:

| History | Scored webhook `data` |
| - | - |
| `score` | `risk_score` |
| `ip`, `country` | `public_ip.ip`, `public_ip.country` |
| `is_vpn`, `is_proxy`, `is_antidetect` | `detection_flags.vpn`, `.proxy`, `.anti_detect_browser` |
| `traffic_channel`, `entry_url` | `traffic_source.channel`, `.landing_url` |
| `created_at` | `observed_at` |

History rows include identity, score, selected `is_*` flags and optional traffic-attribution fields. They do **not** expose the full webhook flag catalog. Server SDKs can normalize either operating-system mismatch result into `detection_flags.os_mismatch`. Use webhook `signals` for the weighted explanation of a new check.

## Pagination

Search by the pseudonymous User HID and page with `offset`:

```js theme={null}
async function readAccountHistory(userHid, maxRows = 500) {
  const rows = [];
  let total = 0;
  while (rows.length < maxRows) {
    const limit = Math.min(100, maxRows - rows.length);
    const url = new URL(
      `/api/v1/history/user_hid/${encodeURIComponent(userHid)}`,
      'https://account.shieldlabs.ai',
    );
    url.searchParams.set('limit', String(limit));
    url.searchParams.set('offset', String(rows.length));
    const response = await fetch(url, {
      headers: { Authorization: `Bearer ${process.env.SHIELDLABS_API_KEY}` },
    });
    if (!response.ok) throw new Error(`History API ${response.status}`);
    const page = await response.json();
    total = page.total;
    rows.push(...page.data);
    if (page.data.length === 0 || rows.length >= total) break;
  }
  return { rows, total, truncated: rows.length < total };
}
```

The example deliberately caps the read and reports truncation. New identifications can arrive during offset pagination; use `identification_id` to deduplicate rows if you save them. Treat a maximum score over these rows as a summary of the retrieved sample, not the analytics dashboard's current identity category.

History reads do not consume identification quota. The History endpoint allows 15 requests per second per domain; back off on `429`. It retrieves individual checks, not a replay of [multi-account group changes](https://docs.shieldlabs.ai/api/webhook-multi-account.md).

## Errors

| Status | Code or body | Meaning |
| - | - | - |
| `400` | `code: "invalid_request"` | Unsupported search type, malformed identifier or invalid pagination |
| `401` | `error` message | Missing or invalid Private API Key, or disabled domain |
| `429` | Rate-limit response | Domain request limit reached; retry with bounded backoff |
| `500` | `code: "internal_error"`, or authentication error | History or authentication lookup failed |

A `200` response with an empty `data` array is not an API error. Do not assume that every error body includes `code`. Authentication middleware can return JSON text with a non-JSON content type.


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