## GET /api/v2/vps/{id}/ddos-scrubbing/traffic

**Get DDoS filtering measurements**

Return what the DDoS filter measured for the VPS primary IPv4 address: traffic that arrived for the address, what was delivered to the VPS and what the filter stopped, as a time series, together with the attacks seen in the window. Get `{id}` from `GET /api/v2/vps` `data[].id`. Measurements exist only for periods in which DDoS filtering was active for the address (see `GET /api/v2/vps/{id}/ddos-scrubbing`: an on-demand session, or `networkMitigation.action` `"scrub"`), arrive once a minute and are kept for 35 days. They are the filter's own packet and byte counters, not samples. `stopped` is arrived minus delivered: attack traffic, and the connection attempts the filter answers on the server's behalf. A step with null rates was not measured; it does not mean there was no traffic. `current` is the newest minute and is null when that minute is more than five minutes old. An attack starts when attack traffic averages at least 2,000 packets per second or 20 Mbps over a minute, and ends when it has stayed under 500 packets per second and 5 Mbps for 15 minutes; `endReason` `"filtering_ended"` means filtering was switched off first. While `networkMitigation.action` is `"blackhole"` nothing reaches the filter, so nothing is measured.

### Related Endpoints

- `GET /api/v2/vps/{id}/ddos-scrubbing`: Get DDoS scrubbing status
- `POST /api/v2/vps/{id}/ddos-scrubbing`: Enable DDoS scrubbing
- `DELETE /api/v2/vps/{id}/ddos-scrubbing`: Disable DDoS scrubbing

### Headers

- `Accept`: application/json
- `Authorization`: Bearer YOUR_API_KEY
- Required API scope: `read:vm`

### Parameters

- `timeframe` (query, string): Window to load. Defaults to `hour`. Aliases `1h`, `24h`, `1d`, `7d` and `30d` are accepted. Other values are rejected with 400 `invalid_request`.
  Allowed values: hour, day, week, month
- `id` (path, string, required): Public VPS ID. Get it from `GET /api/v2/vps` `data[].id`. Do not invent this value; use the exact ID returned by the referenced API response. Example: `vps_01hxa3b4c5d6e7f8g9h0j1k2m3`

### Request Example

```bash
curl -X GET "https://cloud.hostup.se/api/v2/vps/vps_01hxa3b4c5d6e7f8g9h0j1k2m3/ddos-scrubbing/traffic" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
```

### Response Schema

- `id` (string, required): Public VPS ID. Example: `vps_01hxa3b4c5d6e7f8g9h0j1k2m3`
- `ip` (string, required, nullable): The VPS primary IPv4 address the measurements are for. Null when the VPS has none.
- `timeframe` (string, required): The window that was returned.
  Allowed values: hour, day, week, month
- `telemetry` (object, required): Whether the DDoS filter measured anything for this VPS in the window.
- `telemetry.available` (boolean, required): True when at least one step in the window was measured.
- `telemetry.reason` (string, required, nullable): Why nothing is shown, or null when measurements are available. Unavailable means not measured, never that no traffic occurred.
- `window` (object, required)
- `window.startAt` (string, required)
- `window.endAt` (string, required): End of the step in progress.
- `window.sampleCount` (integer, required): Number of entries in `samples`.
- `window.aggregationWindowSeconds` (integer, required): Length of one step: 60 for hour, 300 for day, 1800 for week, 7200 for month.
- `lastMeasuredAt` (string, required, nullable): End of the newest measured minute in the last 24 hours, or null. Measurements arrive once a minute while filtering is active.
- `current` (object, required, nullable): The newest measured minute. Null when the newest measurement is more than five minutes old, which is the case whenever filtering is not active.
- `summary` (object, required): Totals over the measured steps of the window.
- `summary.totalInboundGb` (number, required, nullable): Volume that arrived for the address, in decimal gigabytes. Null when nothing was measured or only packets were counted.
- `summary.totalDeliveredGb` (number, required, nullable): Volume passed on to the VPS, in decimal gigabytes.
- `summary.totalStoppedGb` (number, required, nullable): Volume the filter kept away from the VPS, in decimal gigabytes.
- `summary.inboundPacketCount` (integer, required)
- `summary.deliveredPacketCount` (integer, required)
- `summary.stoppedPacketCount` (integer, required)
- `summary.peakStoppedMbps` (number, required, nullable): Highest ten-second rate of stopped traffic in the window, in megabits per second.
- `summary.peakStoppedPps` (number, required, nullable): Highest ten-second rate of stopped packets per second in the window.
- `samples` (array<object>, required): One entry per step from `window.startAt` to `window.endAt`, oldest first. A step in which nothing was measured has null rates.
- `samples[].recordedAt` (string, required): Start of the step.
- `samples[].inboundMbps` (number, required, nullable): Traffic that arrived for the address, in megabits per second. Null when the step was not measured or only packets were counted.
- `samples[].deliveredMbps` (number, required, nullable): Traffic passed on to the VPS, in megabits per second.
- `samples[].stoppedMbps` (number, required, nullable): Traffic the filter kept away from the VPS (arrived minus delivered), in megabits per second.
- `samples[].inboundPps` (number, required, nullable): Packets per second that arrived for the address. Null when the step was not measured.
- `samples[].deliveredPps` (number, required, nullable): Packets per second passed on to the VPS.
- `samples[].stoppedPps` (number, required, nullable): Packets per second the filter kept away from the VPS.
- `attacks` (array<object>, required): Attacks that were ongoing or ended inside the window, newest first, at most 20.
- `attacks[].startedAt` (string, required): Start of the first minute with attack traffic.
- `attacks[].endedAt` (string, required, nullable): End of the last minute with attack traffic. Null while the attack is ongoing.
- `attacks[].status` (string, required): "ongoing" until attack traffic has been absent for 15 minutes or filtering is switched off.
  Allowed values: ongoing, ended
- `attacks[].endReason` (string, required, nullable): "attack_stopped": the attack traffic went away. "filtering_ended": filtering was switched off while the attack was still running, so its real end was not observed. Null while ongoing.
  Allowed values: attack_stopped, filtering_ended
- `attacks[].peakStoppedMbps` (number, required, nullable): Highest rate of stopped traffic during the attack, in megabits per second. Null when only packets were counted.
- `attacks[].peakStoppedPps` (number, required, nullable): Highest rate of stopped packets per second during the attack.
- `attacks[].stoppedPacketCount` (integer, required): Packets the filter kept away from the VPS during the attack.
- `attacks[].stoppedGb` (number, required, nullable): Volume the filter kept away from the VPS during the attack, in decimal gigabytes. Null when only packets were counted.
- `attacks[].vectors` (array<object>, required): What the attack consisted of, largest share first. Shares under 1 % are left out.
- `attacks[].vectors[].type` (string, required): "syn_flood": fake connection attempts. "tcp_flood": TCP packets that belong to no connection (ACK and RST floods). "udp_flood": UDP above the filter's limit. "fragmented_packets": IP or UDP fragments. "icmp_flood": ICMP above the limit. "repeated_pattern": senders blocked for repeating an attack pattern. "invalid_packets": malformed packets or impossible TCP flags. "other_protocols": other IP protocols above the limit.
  Allowed values: syn_flood, tcp_flood, udp_flood, fragmented_packets, icmp_flood, repeated_pattern, invalid_packets, other_protocols
- `attacks[].vectors[].sharePercent` (number, required): Share of the attack's packets, 0 to 100.
- `attacks[].attackedPorts` (array<integer>, required): Destination ports the filter identified as targets, most frequent first. Empty when none were identified.
- `attacks[].blockedSenderCount` (integer, required, nullable): Sending addresses the filter blocked for repeating the attack pattern. Null when none were reported.

### Responses

#### 200 - DDoS filtering measurements for the window.
```json
{
  "id": "vps_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "ip": "192.0.2.10",
  "timeframe": "hour",
  "telemetry": {
    "available": true,
    "reason": null
  },
  "window": {
    "startAt": "2026-10-06T09:31:00.000Z",
    "endAt": "2026-10-06T10:31:00.000Z",
    "sampleCount": 60,
    "aggregationWindowSeconds": 60
  },
  "lastMeasuredAt": "2026-10-06T10:30:00.000Z",
  "current": {
    "recordedAt": "2026-10-06T10:29:00.000Z",
    "inboundMbps": 6.712,
    "deliveredMbps": 6.105,
    "stoppedMbps": 0.607,
    "inboundPps": 2140.5,
    "deliveredPps": 1012.3,
    "stoppedPps": 1128.2
  },
  "summary": {
    "totalInboundGb": 4.318,
    "totalDeliveredGb": 2.61,
    "totalStoppedGb": 1.708,
    "inboundPacketCount": 31845210,
    "deliveredPacketCount": 3711004,
    "stoppedPacketCount": 28134206,
    "peakStoppedMbps": 48.2,
    "peakStoppedPps": 71350
  },
  "samples": [
    {
      "recordedAt": "2026-10-06T09:31:00.000Z",
      "inboundMbps": 5.904,
      "deliveredMbps": 5.899,
      "stoppedMbps": 0.005,
      "inboundPps": 960.2,
      "deliveredPps": 952.1,
      "stoppedPps": 8.1
    },
    {
      "recordedAt": "2026-10-06T09:32:00.000Z",
      "inboundMbps": null,
      "deliveredMbps": null,
      "stoppedMbps": null,
      "inboundPps": null,
      "deliveredPps": null,
      "stoppedPps": null
    }
  ],
  "attacks": [
    {
      "startedAt": "2026-10-06T09:47:00.000Z",
      "endedAt": "2026-10-06T10:04:00.000Z",
      "status": "ended",
      "endReason": "attack_stopped",
      "peakStoppedMbps": 48.2,
      "peakStoppedPps": 71350,
      "stoppedPacketCount": 28102877,
      "stoppedGb": 1.706,
      "vectors": [
        {
          "type": "syn_flood",
          "sharePercent": 83.2
        },
        {
          "type": "udp_flood",
          "sharePercent": 16.8
        }
      ],
      "attackedPorts": [
        443
      ],
      "blockedSenderCount": 1071
    }
  ]
}
```

#### 400 - Invalid request. The response body is an RFC 7807 Problem Details document.
```json
{
  "type": "https://developer.hostup.se/errors/invalid_request",
  "title": "Invalid request",
  "status": 400,
  "detail": "The request body failed validation.",
  "code": "invalid_request",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z",
  "errors": [
    {
      "pointer": "/items/0/domainName",
      "detail": "`domainName` is required.",
      "code": "invalid_request"
    }
  ]
}
```

#### 401 - Unauthorized. Authentication is required.
```json
{
  "type": "https://developer.hostup.se/errors/unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Authentication is required.",
  "code": "unauthorized",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 403 - Forbidden. The caller lacks a required scope or does not own the resource.
```json
{
  "type": "https://developer.hostup.se/errors/forbidden",
  "title": "Forbidden",
  "status": 403,
  "detail": "The caller lacks a required scope or does not own the resource.",
  "code": "forbidden",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 404 - Not found. The resource does not exist or is not owned by the caller; `code` is `vps_not_found`.
```json
{
  "type": "https://developer.hostup.se/errors/vps_not_found",
  "title": "Not found",
  "status": 404,
  "detail": "The requested resource could not be found.",
  "code": "vps_not_found",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 429 - Rate limited. Retry after the limit resets. 429 responses include `Retry-After` seconds plus `X-RateLimit-*` headers.
```json
{
  "type": "https://developer.hostup.se/errors/rate_limit_exceeded",
  "title": "Too many requests",
  "status": 429,
  "detail": "Too many requests. Retry after the limit resets.",
  "code": "rate_limit_exceeded",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 500 - Internal error. Retry later or contact support if the issue persists.
```json
{
  "type": "https://developer.hostup.se/errors/internal_error",
  "title": "Internal server error",
  "status": 500,
  "detail": "An unexpected error occurred. Retry later or contact support if the issue persists.",
  "code": "internal_error",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```
