# Polling without webhooks (/guides/polling)



Webhooks push events to your server the moment they happen and are included
from the Growth plan up. On Starter, or anywhere you would rather not run a
public endpoint, poll instead. You lose nothing but latency: the same event
log that feeds webhooks feeds this.

## One call per tick [#one-call-per-tick]

```http
GET /v1/changes?after=918273
Authorization: Bearer mplive_…
```

```jsonc
{
  "object": "changes",
  "livemode": true,
  "invoices": [
    {
      "id": "inv_01K3…",
      "last_event": "invoice.paid",
      "sequence": 918301,
      "updated": "2026-09-23T10:41:12.004Z",
      "data": { /* the invoice, exactly as GET /v1/invoices/inv_01K3… returns it */ }
    }
  ],
  "deposits": [ /* same shape */ ],
  "payouts":  [ /* same shape */ ],
  "customers": [],
  "wallets": [],
  "other": [ /* raw events with no resource shape: balance transfers, conversions… */ ],
  "events_scanned": 12,
  "has_more": false,
  "next_after": 918301
}
```

Store `next_after`. Send it back as `after` next time. That is the whole
protocol.

## The rules that make it safe [#the-rules-that-make-it-safe]

* **The cursor is the event `sequence`** — a per-project counter that only
  goes up. Two ticks can never see the same change twice or skip one, however
  long you were down. Gaps in the numbers are normal.
* **Each resource appears once, in its latest state.** An invoice created,
  quoted and paid between two ticks comes back as one paid invoice;
  `last_event` says which event that was. You never have to replay a sequence
  of events to work out where something ended up.
* **`data` is the REST shape.** The same deserializer you wrote for
  `GET /v1/invoices/{id}` reads it.
* **`has_more: true` means the page filled before the log ran out.** Call
  again immediately with the new `next_after`; do not wait for the next cron
  tick. A call scans 200 events by default and at most 500 (`limit`).
* **Filter by type if you only care about money in:**
  `?after=…&type=invoice.paid&type=deposit.confirmed`.

## The first call [#the-first-call]

Send nothing to read the whole log from the beginning, or
`?since=2026-09-23T00:00:00Z` to start from an instant. Either way the
response's `next_after` is what you store; from then on you only ever send
`after`.

## Which key [#which-key]

Any key with the `webhooks:read` scope — it is the log-reading scope and is
granted by default. Live keys read live changes, test keys read test changes.

## When you move to webhooks [#when-you-move-to-webhooks]

Nothing to re-learn: a webhook envelope carries the same `sequence`, so the
cursor you stored is where you reconcile from. Keep the poller running for a
day as a check that every event arrived, then switch it off.
