# Get a lead's activity

Source: https://help.ruuster.com/api/leads-activity

```http
GET https://api.ruuster.com/v1/leads/{leadId}/activity
```

What one lead did, by day, week or month, and optionally the individual events (the same events webhooks send).

- Authentication: `x-api-key` and `x-email` headers of an owner or admin
- Rate limit: 120 requests per minute per key

## Path parameters

| Name | Type | Description |
|---|---|---|
| `leadId` | string | `user:<uuid>` or `unregistered:<uuid>`, exactly as returned in `leadId` |

## Query parameters

| Name | Type | Default | Description |
|---|---|---|---|
| `from` | date | 30 days before `to` | First day, `YYYY-MM-DD` in America/Chicago, included |
| `to` | date | today | Day after the last day, `YYYY-MM-DD`, excluded |
| `granularity` | string | `day` | `day`, `week` (Monday start) or `month` |
| `events` | `0` \| `1` | `0` | `1` adds the lead's individual events from the last 30 days, newest first, 500 per page. This part is a *lookup* call |
| `cursor` | string | | `meta.nextCursor` from the previous page of events |

## Response

Each bucket in `data.series`, and `data.totals`, contain `actions`, `propertyViews`, `favorites`, `savedSearchEvents`, `searches`, `emailsSent`, `emailOpens`, `emailClicks`, `tourRequests`, `contactAgentClicks`, `logins` and `shares`, plus `firstAt` and `lastAt` per bucket.

- `actions` counts recorded activities: listing views, searches, favorites, saved-search changes, tour and contact requests, sign-ins, shares, and human email opens and clicks. Other browsing isn't counted.
- Email opens and clicks leave out opens that look automated, such as mail scanners and security tools. That filter is best-effort. `emailsSent`, `emailOpens` and `emailClicks` are counted on the day the email was sent.

With `events=1`, `data.events` lists events with `id`, `kind`, `at` and the same fields as the matching webhook. Kinds: `lead.property_view`, `lead.favorite`, `lead.saved_search_created`, `lead.saved_search_updated`, `lead.search`, `lead.email_open`, `lead.email_click`, `lead.tour_request`, `lead.contact_agent`, `lead.login`, `lead.share`, `lead.email_status_changed`. The `property` object names any listing involved, yours or not, by `propertyId`, `mlsRecordId`, `mlsName` and `address`, with `ownedByYou`; [Get a listing](https://help.ruuster.com/api/properties-get) returns its details and photos. See [Event catalog](https://help.ruuster.com/api/webhook-events).

## Example request

cURL:

```bash
curl -s "https://api.ruuster.com/v1/leads/user:c0ff3e21-8a4b-4c7d-9e15-2b6f8d3a1c04/activity?from=2026-09-28&to=2026-10-01&granularity=day&events=1" \
  -H "x-api-key: $RUUSTER_API_KEY" \
  -H "x-email: $RUUSTER_EMAIL"
```

Node.js:

```javascript
const res = await fetch("https://api.ruuster.com/v1/leads/user:c0ff3e21-8a4b-4c7d-9e15-2b6f8d3a1c04/activity?from=2026-09-28&to=2026-10-01&granularity=day&events=1", {
  headers: {
    "x-api-key": process.env.RUUSTER_API_KEY,
    "x-email": process.env.RUUSTER_EMAIL,
  },
});
const body = await res.json();
if (!body.ok) throw new Error(`${body.error.code}: ${body.error.message} (${body.requestId})`);
console.log(body.data);
```

Python:

```python
import os
import requests

res = requests.get(
    "https://api.ruuster.com/v1/leads/user:c0ff3e21-8a4b-4c7d-9e15-2b6f8d3a1c04/activity?from=2026-09-28&to=2026-10-01&granularity=day&events=1",
    headers={"x-api-key": os.environ["RUUSTER_API_KEY"], "x-email": os.environ["RUUSTER_EMAIL"]},
    timeout=30,
)
body = res.json()
if not body["ok"]:
    raise RuntimeError(f'{body["error"]["code"]}: {body["error"]["message"]} ({body.get("requestId")})')
print(body["data"])
```

## Example response

```json
{
  "ok": true,
  "data": {
    "leadId": "user:c0ff3e21-8a4b-4c7d-9e15-2b6f8d3a1c04",
    "series": [
      { "bucket": "2026-09-28", "actions": 12, "propertyViews": 7, "favorites": 1, "savedSearchEvents": 0, "searches": 3,
        "emailsSent": 2, "emailOpens": 1, "emailClicks": 0, "tourRequests": 0, "contactAgentClicks": 0, "logins": 1, "shares": 0,
        "firstAt": "2026-09-28T13:02:00Z", "lastAt": "2026-09-28T22:41:19Z" },
      { "bucket": "2026-09-29", "actions": 18, "propertyViews": 9, "favorites": 2, "savedSearchEvents": 1, "searches": 4,
        "emailsSent": 2, "emailOpens": 1, "emailClicks": 1, "tourRequests": 1, "contactAgentClicks": 0, "logins": 1, "shares": 0,
        "firstAt": "2026-09-29T14:15:40Z", "lastAt": "2026-09-30T02:19:03Z" },
      { "bucket": "2026-09-30", "actions": 11, "propertyViews": 6, "favorites": 0, "savedSearchEvents": 0, "searches": 2,
        "emailsSent": 1, "emailOpens": 0, "emailClicks": 0, "tourRequests": 0, "contactAgentClicks": 0, "logins": 0, "shares": 1,
        "firstAt": "2026-09-30T15:30:08Z", "lastAt": "2026-09-30T23:58:44Z" }
    ],
    "totals": { "actions": 41, "propertyViews": 22, "favorites": 3, "savedSearchEvents": 1, "searches": 9,
                "emailsSent": 5, "emailOpens": 2, "emailClicks": 1, "tourRequests": 1, "contactAgentClicks": 0, "logins": 2, "shares": 1 },
    "events": [
      { "id": "evt_3e8c51b0d27a4f96", "kind": "lead.share", "at": "2026-09-30T23:58:44Z",
        "property": { "propertyId": "5b7e2d94-1c63-4a8f-b5e0-9d3c7a2f1e86", "mlsRecordId": "22418765", "address": "4182 E Saguaro Bloom Dr, Tucson, AZ 85718", "ownedByYou": true },
        "channel": "sms" },
      { "id": "evt_9a17f2c4b8e05d31", "kind": "lead.tour_request", "at": "2026-09-30T02:19:03Z",
        "property": { "propertyId": "5b7e2d94-1c63-4a8f-b5e0-9d3c7a2f1e86", "mlsRecordId": "22418765", "address": "4182 E Saguaro Bloom Dr, Tucson, AZ 85718", "ownedByYou": true },
        "tourDate": "2026-10-04", "partOfDay": "morning" },
      { "id": "evt_5c0d8e93a1f74b26", "kind": "lead.email_click", "at": "2026-09-29T14:15:40Z",
        "email": { "emailId": "em_7Yq2pX8mT4cW", "subject": "3 new homes match \"Catalina Foothills 3bd\"", "savedSearchName": "Catalina Foothills 3bd", "sentAt": "2026-09-29T12:00:31Z" } }
    ]
  },
  "meta": { "range": { "from": "2026-09-28", "to": "2026-10-01", "tz": "America/Chicago" }, "granularity": "day", "eventsTotal": 41, "nextCursor": "cur_Zq3w0PPq7Lm3XvT8wR" }
}
```
