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

# Polling for status

> A robust polling loop that won't drop records or hammer the API.

Until webhooks ship in v2, polling is the way to learn that a
customer's KYC or AML status changed. Done well, polling is
**deterministic** (never misses a record) and **frugal** (only one or
two API calls per polling interval).

## The pattern in one paragraph

Track a "high water mark" timestamp in your own database. Each poll
asks for customers updated **after** that timestamp, walks all pages,
and advances the watermark to the latest `updated_at` you saw. That's
it.

## Implementation

```javascript theme={null}
async function pollChangedCustomers({ watermark, apiKey }) {
  let cursor = null;
  let newWatermark = watermark;

  do {
    const url = new URL('https://app.instantcompliance.ai/api/v1/customers');
    url.searchParams.set('limit', '100');
    url.searchParams.set('updated_since', watermark);
    if (cursor) url.searchParams.set('cursor', cursor);

    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${apiKey}` }
    });

    if (res.status === 429) {
      const wait = Number(res.headers.get('Retry-After') ?? 30);
      await new Promise((r) => setTimeout(r, wait * 1000));
      continue;
    }
    if (!res.ok) throw new Error(`Poll failed: ${res.status}`);

    const { data, has_more, next_cursor } = await res.json();
    for (const customer of data) {
      await upsertIntoYourCrm(customer);
      if (customer.updated_at > newWatermark) {
        newWatermark = customer.updated_at;
      }
    }
    if (!has_more) break;
    cursor = next_cursor;
  } while (true);

  return newWatermark;
}
```

Persist `newWatermark` after the loop completes. On the next run,
start from where you left off.

## How often should I poll?

| Use case                       | Interval      |
| ------------------------------ | ------------- |
| Customer-facing status display | 1–5 minutes   |
| Internal compliance dashboard  | 5–15 minutes  |
| CRM sync                       | 15–60 minutes |
| Daily reporting                | Once per day  |

A 5-minute poll on a typical org generates \~12 calls/hour — well below
the 60/min burst limit.

## Edge cases

* **Resume from crash.** Persist the watermark *after* the records are
  written to your store, not before. Re-runs are safe because every
  customer write is idempotent against your own primary key.
* **Clock skew.** Always use the server's `updated_at` value, never
  `Date.now()`, as the watermark. Otherwise drift between your clock
  and ours causes drops or duplicates.
* **Large catch-up after a long outage.** The loop handles it — there's
  no hard limit on backfill except rate limits. Pace your daily quota.

## Stop polling: v2 webhooks

When [webhooks](/webhooks/overview) ship you can decommission this loop
— we'll push the same events to your endpoint with HMAC signatures and
automatic retries. Until then, polling is the safe path.
