> ## Documentation Index
> Fetch the complete documentation index at: https://docs.anchorbrowser.io/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Anchor Browser provides cloud browsers for AI agents and automation: stealth browsing with residential proxies, managed authentication into third-party web apps, and browser sessions that run reliably at scale. AI agents that need Anchor credentials should start with https://docs.anchorbrowser.io/quickstart/agent-access. This flow lets agents obtain an API key programmatically without creating a dashboard account. Read that page before attempting authentication or API access.

# Volumes

> Persistent storage that follows your browser sessions

A volume is a folder your team owns that outlives any single browser session. Mount it into a session and everything the browser downloads is written to it; upload files to it beforehand and the browser can use them; read the results back through the API afterwards. Several sessions can mount the same volume at once and see each other's files immediately.

For the full endpoint list, see the interactive API documentation: [Volumes](/api-reference/volumes) for creating and managing volumes, and [Volume Files](/api-reference/volume-files) for the files inside them.

## How it works

* Files live under the volume root. The browser saves downloads into its `downloads/` folder, so a file downloaded in a session shows up as `downloads/<name>` in the volume.
* Uploads through the API appear inside running sessions within seconds. Files written by a session appear in the API about a minute after the session stops writing to them.
* A volume can be mounted by any number of sessions, read-write or read-only.

<Steps>
  <Step title="Create a volume">
    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://api.anchorbrowser.io/v1/volumes \
        -H "anchor-api-key: $ANCHOR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{"name": "customer-reports"}'
      ```

      ```javascript node.js theme={null}
      const response = await fetch('https://api.anchorbrowser.io/v1/volumes', {
        method: 'POST',
        headers: { 'anchor-api-key': process.env.ANCHOR_API_KEY, 'Content-Type': 'application/json' },
        body: JSON.stringify({ name: 'customer-reports' }),
      });
      const { data: volume } = await response.json();
      console.log(volume.id, volume.state); // state is "ready" when the volume can be used
      ```

      ```python python theme={null}
      import os, requests

      response = requests.post(
          "https://api.anchorbrowser.io/v1/volumes",
          headers={"anchor-api-key": os.environ["ANCHOR_API_KEY"]},
          json={"name": "customer-reports"},
      )
      volume = response.json()["data"]
      print(volume["id"], volume["state"])  # state is "ready" when the volume can be used
      ```
    </CodeGroup>

    Names are unique within your team and may contain letters, numbers, `_` and `-`. If the response says `creating`, poll [get volume](/api-reference/volumes/get-volume) until it is `ready`.
  </Step>

  <Step title="Upload files the browser should have (optional)">
    Send a multipart request with the file and the destination `path`. Put files under `downloads/` if a page in the session will upload them.

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://api.anchorbrowser.io/v1/volumes/$VOLUME_ID/files \
        -H "anchor-api-key: $ANCHOR_API_KEY" \
        -F "file=@./contract.pdf" \
        -F "path=downloads/contract.pdf"
      ```

      ```javascript node.js theme={null}
      import fs from 'fs';

      const form = new FormData();
      form.append('file', new Blob([fs.readFileSync('./contract.pdf')]), 'contract.pdf');
      form.append('path', 'downloads/contract.pdf');

      await fetch(`https://api.anchorbrowser.io/v1/volumes/${volumeId}/files`, {
        method: 'POST',
        headers: { 'anchor-api-key': process.env.ANCHOR_API_KEY },
        body: form,
      });
      ```

      ```python python theme={null}
      with open("./contract.pdf", "rb") as f:
          requests.post(
              f"https://api.anchorbrowser.io/v1/volumes/{volume_id}/files",
              headers={"anchor-api-key": os.environ["ANCHOR_API_KEY"]},
              files={"file": ("contract.pdf", f)},
              data={"path": "downloads/contract.pdf"},
          )
      ```
    </CodeGroup>

    <Note>
      Multipart uploads are limited to 10 MB. For larger files request a pre-signed URL with [create upload URL](/api-reference/volume-files/create-upload-url) and `PUT` the file to it, sending the returned headers unchanged.
    </Note>
  </Step>

  <Step title="Start a session with the volume mounted">
    Add `browser.volume` to the [session configuration](/api-reference/browser-sessions/start-browser-session).

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://api.anchorbrowser.io/v1/sessions \
        -H "anchor-api-key: $ANCHOR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{"browser": {"volume": {"id": "'$VOLUME_ID'"}}}'
      ```

      ```javascript node.js theme={null}
      const session = await fetch('https://api.anchorbrowser.io/v1/sessions', {
        method: 'POST',
        headers: { 'anchor-api-key': process.env.ANCHOR_API_KEY, 'Content-Type': 'application/json' },
        body: JSON.stringify({
          browser: {
            volume: {
              id: volumeId,
              // read_only: true,        // mount without write access
            },
          },
        }),
      }).then((r) => r.json());
      ```

      ```python python theme={null}
      session = requests.post(
          "https://api.anchorbrowser.io/v1/sessions",
          headers={"anchor-api-key": os.environ["ANCHOR_API_KEY"]},
          json={
              "browser": {
                  "volume": {
                      "id": volume_id,
                      # "read_only": True,        # mount without write access
                  }
              }
          },
      ).json()
      ```
    </CodeGroup>

    The session starts once the volume is mounted, which adds a few seconds to session creation.
  </Step>

  <Step title="Download in the browser">
    Nothing changes in how you drive the browser. Any download lands in the volume's `downloads/` folder.

    <CodeGroup>
      ```tsx node.js theme={null}
      await page.goto("https://browser-tests-alpha.vercel.app/api/download-test");
      await Promise.all([page.waitForEvent("download"), page.locator("#download").click()]);
      // The file is now in the volume as downloads/<filename>
      ```

      ```python python theme={null}
      await page.goto("https://browser-tests-alpha.vercel.app/api/download-test")
      async with page.expect_download():
          await page.locator("#download").click()
      # The file is now in the volume as downloads/<filename>
      ```
    </CodeGroup>

    <Note>
      To download PDF files instead of viewing them in the browser, set `browser.pdf_viewer.active` to `false` when creating the session.
    </Note>
  </Step>

  <Step title="Use a volume file in a web form">
    Files under `downloads/` can be attached to a file input by their `/downloads/<name>` path through CDP, whether they were uploaded through the API or downloaded by another session.

    <CodeGroup>
      ```tsx node.js theme={null}
      await page.goto('https://browser-tests-alpha.vercel.app/api/upload-test');

      const cdp = await page.context().newCDPSession(page);
      const { root } = await cdp.send('DOM.getDocument');
      const { nodeId } = await cdp.send('DOM.querySelector', { nodeId: root.nodeId, selector: '#fileUpload' });
      await cdp.send('DOM.setFileInputFiles', { nodeId, files: ['/downloads/contract.pdf'] });
      ```

      ```python python theme={null}
      await page.goto("https://browser-tests-alpha.vercel.app/api/upload-test")

      cdp = await page.context.new_cdp_session(page)
      root = await cdp.send("DOM.getDocument")
      node = await cdp.send("DOM.querySelector", {"nodeId": root["root"]["nodeId"], "selector": "#fileUpload"})
      await cdp.send("DOM.setFileInputFiles", {"nodeId": node["nodeId"], "files": ["/downloads/contract.pdf"]})
      ```
    </CodeGroup>
  </Step>

  <Step title="Read the results after the session">
    List the folder, then fetch a file through its pre-signed URL.

    <CodeGroup>
      ```bash curl theme={null}
      curl "https://api.anchorbrowser.io/v1/volumes/$VOLUME_ID/files?path=downloads" \
        -H "anchor-api-key: $ANCHOR_API_KEY"

      # Follow the redirect straight to the file
      curl -L "https://api.anchorbrowser.io/v1/volumes/$VOLUME_ID/files/download?path=downloads/report.pdf&redirect=true" \
        -H "anchor-api-key: $ANCHOR_API_KEY" -o report.pdf
      ```

      ```javascript node.js theme={null}
      const headers = { 'anchor-api-key': process.env.ANCHOR_API_KEY };
      const base = `https://api.anchorbrowser.io/v1/volumes/${volumeId}`;

      const { data: listing } = await fetch(`${base}/files?path=downloads`, { headers }).then((r) => r.json());
      console.log(listing.items); // [{ path: 'downloads/report.pdf', type: 'file', size: 142786, ... }]

      const { data: link } = await fetch(`${base}/files/download?path=downloads/report.pdf`, { headers }).then((r) => r.json());
      const file = await fetch(link.signed_url); // no API key needed; the URL expires after link.expires_in seconds
      ```

      ```python python theme={null}
      headers = {"anchor-api-key": os.environ["ANCHOR_API_KEY"]}
      base = f"https://api.anchorbrowser.io/v1/volumes/{volume_id}"

      listing = requests.get(f"{base}/files", headers=headers, params={"path": "downloads"}).json()["data"]
      print(listing["items"])  # [{"path": "downloads/report.pdf", "type": "file", "size": 142786, ...}]

      link = requests.get(f"{base}/files/download", headers=headers, params={"path": "downloads/report.pdf"}).json()["data"]
      pdf = requests.get(link["signed_url"]).content  # no API key needed; the URL expires after link["expires_in"] seconds
      ```
    </CodeGroup>

    Files written by a session become visible here about a minute after the session stops writing to them, so poll the listing if the session just finished.
  </Step>
</Steps>

## Sharing a volume between sessions

Sessions that mount the same volume share it live: a file downloaded in one session can be used in another a few seconds later, without going through the API. Mount the volume read-only in sessions that should only consume files.

## Managing volumes

| Action                          | Endpoint                                                                  |
| ------------------------------- | ------------------------------------------------------------------------- |
| List your volumes               | `GET /v1/volumes` (returns `{ count, items }`)                            |
| Get one by ID or name           | `GET /v1/volumes/{volume_id}`, `GET /v1/volumes/by-name/{name}`           |
| List a folder or the whole tree | `GET /v1/volumes/{volume_id}/files?path=&recursive=true`                  |
| File metadata                   | `GET /v1/volumes/{volume_id}/files/stat?path=`                            |
| Create a folder                 | `POST /v1/volumes/{volume_id}/directories`                                |
| Delete a file or a folder       | `DELETE /v1/volumes/{volume_id}/files?path=` or `?prefix=&recursive=true` |
| Delete the volume and its files | `DELETE /v1/volumes/{volume_id}`                                          |

Deleting a volume is refused while a running session has it mounted; end those sessions first.

## Limits

* Up to 10 volumes per team.
* One volume per session.
* Multipart uploads up to 10 MB; pre-signed uploads up to 5 GB.
* Paths are relative to the volume root and cannot contain `..`.
* Volumes are not available for [batch sessions](/advanced/batch-browser-sessions).
