# Seeing what uses space

> Scan a scope, then ask the saved scan where the space went, by folder, file, bucket, file type and over time.

These commands read. `scan` walks the disk and saves the result; the others answer from the newest saved scan, or rescan first with `--refresh`. Every one accepts a scope: `--scope drive` (the default), `--scope data`, or `--path PATH`. See [Scans and scopes](https://diskcensus.com/docs/cli/scans.md).

## census scan

**Usage:** `census [scan] [PATH] [SCOPE] [--full] [--no-save] [--workers N] [--backend MODE] [--verbose] [--progress] [--json]`

Scans a scope and saves its tree and report for later queries. `scan` is the default command, so `census` alone scans the startup drive.

| Option | Description |
| --- | --- |
| `--scope KIND` | `drive` (the whole startup drive, the default) or `data` |
| `-p, --path PATH` | Scan this folder. A bare `PATH` does the same. |
| `--full` | Walk the whole scope even when the saved scan could be updated |
| `--no-save` | Don't update the saved scan or history |
| `-j, --workers N` | Traversal workers (1–128) |
| `--backend MODE` | `auto`, `std` or `bulk` |
| `--verbose` | Print scan diagnostics before the summary |
| `--progress` | Write scan progress as NDJSON to stderr |
| `--json` | Write one JSON response |

```sh
# The whole startup drive
census

# One folder, with progress events on stderr
census scan --path /Applications --json --progress 2>progress.ndjson
```

```json
{
  "schema_version": 5,
  "command": "scan",
  "status": "complete",
  "exit_code": 0,
  "errors": [],
  "warnings": ["…"],
  "metrics": { "files_seen": 130141 },
  "read_errors": { "actionable_total": 0 }
}
```

The JSON keeps the full scan report, shortened above. `coverage_details` compares the scan with the disk:

| Field | Meaning |
| --- | --- |
| `capacity.volume_name` | The volume's name in Finder, such as `Macintosh HD`. |
| `capacity.total_bytes`, `used_bytes`, `free_bytes` | The container's size, space in use and free space. `used_bytes` matches `diskutil`. |
| `capacity.purgeable_bytes` | Space macOS can free on its own, which Finder counts as available. `null` when macOS doesn't say. |
| `capacity.hidden_bytes` | Used space in no file the scan could read: snapshots, staged updates, Recovery, protected or unreadable folders. `null` for folder scans. |
| `volumes[].used_bytes`, `volumes[].scanned_bytes` | Each volume's space in use, and what the scan counted on it. |
| `access_hint` | What would let the scan see more: `full_disk_access_required`, `other_accounts_unreadable` (run with `sudo`), or `null`. |

`capacity` is `null` when it couldn't be read.

## census report

**Usage:** `census report [SCOPE] [--refresh] [--progress] [--json]`

Shows the last saved scan report for a scope. `--refresh` scans again first.

```sh
census report
census report --path /Applications --json
```

```json
{
  "schema_version": 5,
  "command": "report",
  "status": "complete",
  "exit_code": 0,
  "errors": [],
  "warnings": ["…"],
  "scan_id": "…",
  "metrics": { "files_seen": 130141 },
  "read_errors": { "actionable_total": 0 }
}
```

## census scans

**Usage:** `census scans [--json]`

Lists every saved scan: its scope, root, scan ID, item count and the size of its saved file.

```sh
census scans
```

## census breakdown

**Usage:** `census breakdown [SCOPE] [--refresh] [--progress] [--json]`

Shows how the scanned space is grouped into storage buckets, such as macOS, Applications, Developer, AI agents, Caches and System Data. For a folder scan, it says that only that folder is included, and the JSON sets `partial_scope` to `true`.

```sh
census breakdown
census breakdown --path /Applications --json
```

```json
{
  "schema_version": 5,
  "command": "breakdown",
  "status": "ok",
  "exit_code": 0,
  "errors": [],
  "warnings": [],
  "results": {
    "buckets": [
      {
        "bucket": "applications",
        "label": "Applications",
        "allocated_bytes": 5491449856,
        "files": 130141,
        "share": 1.0
      }
    ],
    "partial_scope": true,
    "total_allocated_bytes": 5491449856
  }
}
```

## census top

**Usage:** `census top [SCOPE] [--files] [--kind KIND] [--min-size SIZE] [--older-than DAYS] [--under PATH] [--depth N] [--limit N] [--refresh] [--progress] [--json]`

Shows the largest folders. With `--files`, the largest files.

| Option | Description |
| --- | --- |
| `--scope KIND` | `drive` (the default) or `data` |
| `-p, --path PATH` | A folder's own scan |
| `--files` | List files instead of folders |
| `--kind KIND` | With `--files`, only this kind: `video`, `audio`, `images`, `documents`, `archives`, `disk_images`, `code`, `apps`, `app_data` or `other` |
| `--min-size SIZE` | With `--files`, at least this size: bytes, or a `K`, `M` or `G` value |
| `--older-than DAYS` | With `--files`, not modified in the last `DAYS` days |
| `--under PATH` | Only within this folder (default: the scan's root) |
| `--depth N` | Folder depth to look down (default: 3) |
| `--limit N` | Show at most `N` rows (default: 20; `0` means all) |
| `--refresh` | Rescan first; otherwise the saved scan is read |
| `--progress` | With `--refresh`, write scan progress as NDJSON to stderr |
| `--json` | Write one JSON response |

```sh
# Largest folders, three levels deep
census top

# Largest folders inside ~/Applications, two levels deep
census top --under ~/Applications --depth 2

# Code files of 1 MB or more, untouched for a year
census top --files --kind code --min-size 1M --older-than 365 --limit 10
```

The filters combine before the row limit. Files with an unknown modification time never match `--older-than`.

```sh
census top --path /Applications --limit 1 --json
```

```json
{
  "schema_version": 5,
  "command": "top",
  "status": "ok",
  "exit_code": 0,
  "errors": [],
  "warnings": [],
  "scan": { "scope_kind": "path", "scan_id": "…" },
  "results": [
    {
      "path": { "display": "/Applications/Xcode.app/Contents", "lossy": false },
      "allocated_bytes": 3891544064,
      "files": 125827,
      "folders": 29829,
      "share": 0.7087
    }
  ]
}
```

With `--files`, `results` is an object holding `files` and the `filters` you chose:

```json
{
  "results": {
    "files": [
      {
        "path": { "display": "/Applications/ChatGPT.app/Contents/Resources/app.asar", "lossy": false },
        "kind": "apps",
        "allocated_bytes": 480194560,
        "logical_bytes": 480193694
      }
    ],
    "filters": { "kind": null, "min_size_bytes": null, "older_than_days": null }
  }
}
```

## census ls

**Usage:** `census ls [PATH] [SCOPE] [--sort size|name] [--limit N] [--refresh] [--progress] [--json]`

Shows a saved folder's contents, largest first. `PATH` defaults to the scan's root. Without a scope, it reads the newest saved scan that contains `PATH`.

| Option | Description |
| --- | --- |
| `--scope KIND` | `drive` (the default) or `data` |
| `-p, --path PATH` | A folder's own scan |
| `--sort ORDER` | `size` (the default) or `name` |
| `--limit N` | Show at most `N` rows (default: 50; `0` means all) |
| `--refresh` | Rescan first; otherwise the saved scan is read |
| `--progress` | With `--refresh`, write scan progress as NDJSON to stderr |
| `--json` | Write one JSON response |

```sh
census ls ~/Applications --limit 50
census ls ~/Applications --sort name --json
```

```json
{
  "schema_version": 5,
  "command": "ls",
  "status": "ok",
  "exit_code": 0,
  "errors": [],
  "warnings": [],
  "scan": { "scope_kind": "path", "scan_id": "…" },
  "results": {
    "directory": {
      "path": { "display": "/Applications", "lossy": false },
      "files": 130141
    },
    "entries": [
      {
        "name": { "display": "Xcode.app", "lossy": false },
        "kind": "folder",
        "allocated_bytes": 3891544064
      }
    ],
    "truncated": 5
  }
}
```

`truncated` counts the entries `--limit` left out.

## census types

**Usage:** `census types [SCOPE] [--under PATH] [--limit N] [--refresh] [--progress] [--json]`

Shows file types and their share of the scan. It groups files by their package, their Library location, or their extension, without opening them. Apps (`.app` bundles) count as a whole, and libraries such as `.photoslibrary` count as their media kind. The kinds add up to the folder's total.

| Option | Description |
| --- | --- |
| `--scope KIND` | `drive` (the default) or `data` |
| `-p, --path PATH` | A folder's own scan |
| `--under PATH` | Only within this folder. Default: the scan's root for a folder scan, your home folder for a drive or data scan. |
| `--limit N` | Show at most `N` kinds (default: all; `0` means all) |
| `--refresh` | Rescan first; otherwise the saved scan is read |
| `--progress` | With `--refresh`, write scan progress as NDJSON to stderr |
| `--json` | Write one JSON response |

```sh
# File types in your home folder
census types

# File types in /Applications
census types --under /Applications --json
```

```json
{
  "schema_version": 5,
  "command": "types",
  "status": "ok",
  "exit_code": 0,
  "errors": [],
  "warnings": [],
  "results": {
    "root": { "display": "/Applications", "lossy": false },
    "allocated_bytes": 5491449856,
    "files": 130141,
    "truncated": 0,
    "kinds": [
      {
        "kind": "apps",
        "label": "Apps",
        "allocated_bytes": 5491441664,
        "files": 130138,
        "share": 0.9999985
      },
      "…"
    ]
  }
}
```

## census history

**Usage:** `census history [--path PATH] [--record] [--json]`

Shows how the disk's used space changed over the last hour, day, week and month, and which category grew most this week. Each run records a sample when the newest is more than 55 minutes old; scans and drive breakdowns add samples too.

| Option | Description |
| --- | --- |
| `-p, --path PATH` | The volume that holds `PATH` (default: the startup disk) |
| `--record` | Record a space sample now, whatever the newest one's age |
| `--json` | Write one JSON response |

```sh
census history --json
```

```json
{
  "schema_version": 5,
  "command": "history",
  "status": "ok",
  "exit_code": 0,
  "errors": [],
  "warnings": [],
  "results": {
    "volume": {
      "name": "Macintosh HD",
      "uuid": "6F0E5A4C-3C0B-4C8B-9F0E-0123456789AB",
      "total_bytes": 62826479616,
      "used_bytes": 60784128000,
      "free_bytes": 2042351616
    },
    "changes": [
      {
        "period": "hour",
        "since_unix_ms": 1791354381631,
        "exact": true,
        "used_bytes_then": 60664128000,
        "change_bytes": 120000000,
        "change_ratio": 0.002,
        "categories": null
      },
      {
        "period": "week",
        "since_unix_ms": 1790753181631,
        "exact": true,
        "used_bytes_then": 48384128000,
        "change_bytes": 12400000000,
        "change_ratio": 0.256,
        "categories": [
          { "bucket": "developer", "label": "Developer", "change_bytes": 8100000000 }
        ]
      }
    ]
  }
}
```

The full response has one entry per period: `hour`, `day`, `week` and `month`. `exact` is `true` when a sample lies near the start of the period, and `false` when the history is shorter than the period and the oldest sample stands in for it. A period with no sample near its start has `null` for `since_unix_ms` and every number.
