<!-- https://developers.silktide.com/v1/analytics/acquisition/get-sources-over-time | Analytics / Acquisition -->

# Get visitors from the busiest sources, step by step

`GET /v1/results/analytics/sources-over-time`

Draws the busiest sources as lines over time: unique visitors from each, step by step through the period. It is the chart on the Sources screen in Analytics, and takes the [Analytics arguments](https://developers.silktide.com/v1/analytics.md).

Unlike [Get sources](https://developers.silktide.com/v1/analytics/acquisition/get-sources.md), a line is one source across every medium it arrived through. Direct visits are the line whose `key` is empty.

Send a `period` with a start, such as `last-30-days` or `custom` with `from` and `to`. Left out, `period` is `all`, and this query answers with an error.

`limit` counts the `Other` line, which adds up every source after the busiest. With the default of `5`, you get four sources and `Other`. `Other` is left out when every source already has a line.

A line's `total` adds up its steps, so a visitor who came on two different days counts twice in it. For the number of unique visitors over the whole period, use Get sources.

## Request

### Query parameters

- `view` (string, optional, matches `^(all|[1-9]\d*)$`): The analytics view to read: its id, the number after `/analytics/` in its address in Silktide, or `all` for all of the account's analytics. Send this or `websiteId`.
- `websiteId` (integer, optional, at least 1): Instead of `view`, a website whose analytics to read. The website must have Analytics set up.
- `sectionId` (integer, optional): With `websiteId`, a section of the website. Defaults to `0`, the whole website.
- `period` (string, optional): The date range. Defaults to `all`. The spaced forms (`-7 days`, `month to date`) are older names that keep working. `custom` needs `from` and `to`. One of: `all`, `today`, `last-7-days`, `last-30-days`, `last-90-days`, `month-to-date`, `last-month`, `year-to-date`, `last-12-months`, `custom`, `-7 days`, `-30 days`, `-90 days`, `-1 month`, `month to date`, `year to date`, `-12 months`.
- `from` (string, optional): With `period=custom`, the first day, such as `2026-10-01`.
- `to` (string, optional): With `period=custom`, the last day.
- `timezone` (string, optional): The IANA time zone days are read in, such as `America/New_York`. Defaults to `UTC`.
- `filters` (map of strings, optional): Only visits matching every filter, keyed by what is filtered: `filters[url]=contains:/blog`. Each value is `{operator}:{value}`, with operator `is`, `not`, `contains`, `starts`, `gt` or `lt`. Keys include `url`, `hostName`, `entryUrl`, `exitUrl`, `event`, `outgoingUrl`, `referrer`, `source`, `medium`, `campaign`, `content`, `term`, `countryCode`, `regionCode`, `language`, `deviceType` (`desktop`, `tablet` or `mobile`), `operatingSystem` and `browser`.
- `interval` (string, optional): With `period=custom`, the length of each step: an hour, a day, a week starting on Monday, a month or a year. Ignored with any other period. Left out, steps are hours for a single day, months for exactly twelve months, and days otherwise. One of: `PT1H`, `P1D`, `P7D`, `P1M`, `P1Y`.
- `limit` (integer, optional): How many series to return, counting `Other`, from `2` to `50`; a number outside that is taken as the nearest end. Defaults to `5`: the four busiest, then `Other` for the rest.

## Response

Status `200`.

- `result` (object, required): The answer.
  - `multiSeries` (list of objects, required): The busiest sources by visitors, busiest first, then `Other` when there are more than `limit`. Empty when the period had none.
    - `label` (string, required): The source's name, as Silktide shows it: the account's label for it where one is set. `Other` for the rest.
    - `key` (string, required): The source as recorded, the same value as `source` in Get sources: empty for direct visits. `other` for the series that adds up the rest.
    - `total` (integer, required): The visitors of every step added up. A visitor who came in more than one step is counted in each.
    - `series` (list of objects, required): One step per hour, day, week or month of the period, oldest first. A step with no visits reads `0`.
      - `time` (string, required, matches `^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$`): When the step starts, as `YYYY-MM-DD HH:MM:SS` in the request's `timezone`.
      - `visitors` (integer, required): Unique visitors in the step.
  - `range` (object, required): The dates the series covers.
    - `start` (string, required, matches `^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$`): When the first step starts, as `YYYY-MM-DD HH:MM:SS` in the request's `timezone`.
    - `end` (string, required, matches `^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$`): When the range ends, as `YYYY-MM-DD HH:MM:SS`: midnight after its last day, so the range runs up to but not including it.
- `selector` (string, required): The query that answered, as its path: `/v1/results/scores`.
- `duration` (number, required): Seconds Silktide took to answer.

## Errors

| Status | Type | When |
| --- | --- | --- |
| 404 | `ModelNotFound` | No view or website with that id exists in the account, or the API key's user cannot see it. |
| 403 | `AccessDenied` | The API key's user cannot read that analytics view. |
| 200 | `ModelNotFound` | The website has no Analytics set up. The body is an error despite the status. |
| 200 | `InvalidRequest` | Neither `view` nor `websiteId` was sent. The error's own `status` is `400` and `properties.field` is `view`. The body is an error despite the status. |
| 200 | `InvalidArgument` | A filter names a key or operator Analytics does not know. The body is an error despite the status. |
| 200 | `Error` | `period` is `all` or left out, which means `all`. Over-time queries need a period with a start. The body is an error despite the status. |
| 200 | `InvalidArgument` | `period` is not one of its values, or is `custom` without both `from` and `to`. The body is an error despite the status. |
| 200 | `Unspecified` | `interval` is not one of its values. The body is an error despite the status. |
| 401 | `NotLoggedIn` | No API key was sent, or it is not a key for this region. |
| 429 | `TooManyRequests` | Too many requests to this path in five seconds. Retry after the number of seconds in the `Retry-After` header, which `properties.retryAfter` repeats. |
| 503 | - | Silktide is busy. Retry after the number of seconds in the `Retry-After` header. |

## Access

- Permission: View analytics view (View all analytics for `view=all`)
- Plan feature: none
- Edition: Silktide 4 and Silktide 5
- Cost: free
- Stability: stable, since 1.0.0

## Example: The three busiest sources, day by day

```bash
curl "https://api.eu.silktide.com/v1/results/analytics/sources-over-time?view=all&period=custom&from=2026-09-28&to=2026-09-30&limit=3" \
  -H "Authorization: silktide token=$SILKTIDE_API_KEY"
```

Response:

```json
{
  "result": {
    "multiSeries": [
      {
        "label": "Direct",
        "key": "",
        "total": 128,
        "series": [
          {
            "time": "2026-09-28 00:00:00",
            "visitors": 41
          },
          {
            "time": "2026-09-29 00:00:00",
            "visitors": 43
          },
          {
            "time": "2026-09-30 00:00:00",
            "visitors": 44
          }
        ]
      },
      {
        "label": "Google",
        "key": "Google",
        "total": 93,
        "series": [
          {
            "time": "2026-09-28 00:00:00",
            "visitors": 26
          },
          {
            "time": "2026-09-29 00:00:00",
            "visitors": 35
          },
          {
            "time": "2026-09-30 00:00:00",
            "visitors": 32
          }
        ]
      },
      {
        "label": "Other",
        "key": "other",
        "total": 104,
        "series": [
          {
            "time": "2026-09-28 00:00:00",
            "visitors": 40
          },
          {
            "time": "2026-09-29 00:00:00",
            "visitors": 28
          },
          {
            "time": "2026-09-30 00:00:00",
            "visitors": 36
          }
        ]
      }
    ],
    "range": {
      "start": "2026-09-28 00:00:00",
      "end": "2026-10-01 00:00:00"
    }
  },
  "selector": "/v1/results/analytics/sources-over-time",
  "duration": 0.09
}
```

## Example: The busiest source, week by week

```bash
curl "https://api.eu.silktide.com/v1/results/analytics/sources-over-time?view=all&period=custom&from=2026-07-06&to=2026-07-19&interval=P7D&limit=2" \
  -H "Authorization: silktide token=$SILKTIDE_API_KEY"
```

Response:

```json
{
  "result": {
    "multiSeries": [
      {
        "label": "Google",
        "key": "Google",
        "total": 402,
        "series": [
          {
            "time": "2026-07-06 00:00:00",
            "visitors": 197
          },
          {
            "time": "2026-07-13 00:00:00",
            "visitors": 205
          }
        ]
      },
      {
        "label": "Other",
        "key": "other",
        "total": 731,
        "series": [
          {
            "time": "2026-07-06 00:00:00",
            "visitors": 352
          },
          {
            "time": "2026-07-13 00:00:00",
            "visitors": 379
          }
        ]
      }
    ],
    "range": {
      "start": "2026-07-06 00:00:00",
      "end": "2026-07-20 00:00:00"
    }
  },
  "selector": "/v1/results/analytics/sources-over-time",
  "duration": 0.12
}
```
