# Get website traffic

Source: https://help.ruuster.com/api/traffic

```http
GET https://api.ruuster.com/v1/traffic
```

Google Analytics visits to your Ruuster hostnames by source, medium and campaign. Measured per website, so it can't be split by agent, lead or listing.

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

## 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` or `month`. History goes back to `coverage.ga.from` in `/me` |
| `groupBy` | string | `source,medium` | Any of `source`, `medium`, `campaign`, comma-separated. Empty for totals only |
| `hostname` | string | all yours | One of `hostnames` from `/me` |

## Response

`data.context` is `brokerage` and `data.note` says traffic can't be split by agent, lead or listing. Each row in `data.series`: `bucket`, `source`, `medium`, `campaign`, `sessions`, `users`, `newUsers`, `pageViews`.

- `(not set)` is returned as `null`.
- With `campaign`, the top 50 campaigns per bucket are listed and the rest are combined as `other`.
- `users` isn't additive across rows, so it's `null` in `totals`; in `week` and `month` buckets it's the sum of each day's users.
- Google can revise the last 48 hours.
- Visits from links in Ruuster's own emails show as source `sendGrid`, medium `sendGridEmails`.

## Example request

cURL:

```bash
curl -s "https://api.ruuster.com/v1/traffic?from=2026-09-21&to=2026-09-28&granularity=week&groupBy=source,medium" \
  -H "x-api-key: $RUUSTER_API_KEY" \
  -H "x-email: $RUUSTER_EMAIL"
```

Node.js:

```javascript
const res = await fetch("https://api.ruuster.com/v1/traffic?from=2026-09-21&to=2026-09-28&granularity=week&groupBy=source,medium", {
  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/traffic?from=2026-09-21&to=2026-09-28&granularity=week&groupBy=source,medium",
    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": {
    "context": "brokerage",
    "hostnames": ["saguaro.ruuster.com"],
    "note": "Traffic is measured per hostname by Google Analytics and cannot be split by agent, lead, or property.",
    "series": [
      { "bucket": "2026-09-21", "source": "facebook.com", "medium": "referral", "campaign": null, "sessions": 7504, "users": 7350, "newUsers": 6335, "pageViews": 15260 },
      { "bucket": "2026-09-21", "source": "m.facebook.com", "medium": "referral", "campaign": null, "sessions": 4620, "users": 4424, "newUsers": 2905, "pageViews": 10969 },
      { "bucket": "2026-09-21", "source": "(direct)", "medium": "(none)", "campaign": null, "sessions": 2240, "users": 1905, "newUsers": 830, "pageViews": 8130 },
      { "bucket": "2026-09-21", "source": "sendGrid", "medium": "sendGridEmails", "campaign": null, "sessions": 1980, "users": 1402, "newUsers": 210, "pageViews": 12416 },
      { "bucket": "2026-09-21", "source": "google", "medium": "organic", "campaign": null, "sessions": 612, "users": 540, "newUsers": 401, "pageViews": 1655 },
      { "bucket": "2026-09-21", "source": "other", "medium": "other", "campaign": null, "sessions": 415, "users": 380, "newUsers": 266, "pageViews": 1210 }
    ],
    "totals": { "sessions": 17371, "users": null, "newUsers": 10947, "pageViews": 49640 }
  },
  "meta": { "range": { "from": "2026-09-21", "to": "2026-09-28", "tz": "America/Chicago" }, "granularity": "week",
            "coverage": { "ga": { "from": "2023-06-16", "to": "2026-09-30", "asOf": "2026-10-01T13:30:05Z" } } }
}
```
