> ## Documentation Index
> Fetch the complete documentation index at: https://api-tools.memories.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Download by Bucket and Blob

> Download an asset (or any GCS-stored resource) by bucket and blob path instead of by asset ID.

<Info>
  **Product**: Visual Intelligence — Asset Management
  **Use case**: Stream a stored file directly from GCS using the `bucket` and `blob` pair surfaced by other Visual Intelligence / Visual Search endpoints
  **Host**: `https://mavi-backend.memories.ai/serve/api/v2`
  **Auth**: `Authorization: sk-mavi-...` (no `Bearer` prefix)
</Info>

Download a stored file by its `bucket` and `blob` directly, without first translating to an `asset_id`. The response body is a **binary file stream**; errors are JSON.

This is the location-addressed counterpart of [Download Asset](/visual-intelligence/base/download). Use it when you already have a `bucket` / `blob` pair from a search/list response (e.g. Visual Search returns `video_bucket` / `video_blob` and `keyframe_bucket` / `keyframe_blob` in its v1 query endpoints).

<Note>
  **Pricing:** \$0.12 / GB downloaded — same model as [Download Asset](/visual-intelligence/base/download).
</Note>

### Code Examples

<CodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url 'https://mavi-backend.memories.ai/serve/api/v2/download?bucket=mavi-resource&blob=VI625239098370850816.mp4' \
    --header 'Authorization: sk-mavi-...' \
    --remote-name \
    --remote-header-name
  ```

  ```python Python theme={null}
  import requests, re

  url = "https://mavi-backend.memories.ai/serve/api/v2/download"
  params = {
      "bucket": "mavi-resource",
      "blob": "VI625239098370850816.mp4",
  }
  headers = {"Authorization": "sk-mavi-..."}

  response = requests.get(url, params=params, headers=headers, stream=True)

  if not response.ok or "application/json" in (response.headers.get("Content-Type") or "").lower():
      raise Exception(response.json().get("msg", "Download failed"))

  cd = response.headers.get("Content-Disposition", "")
  m = re.search(r'filename[^;=\n]*=(([\'"]).*?\2|[^;\n]*)', cd)
  filename = m.group(1).strip("'\"") if m else "download"

  with open(filename, "wb") as f:
      for chunk in response.iter_content(chunk_size=8192):
          if chunk:
              f.write(chunk)

  print(f"Saved to: {filename}")
  ```

  ```javascript fetch theme={null}
  const url = new URL("https://mavi-backend.memories.ai/serve/api/v2/download");
  url.searchParams.set("bucket", "mavi-resource");
  url.searchParams.set("blob", "VI625239098370850816.mp4");

  const response = await fetch(url, {
    headers: { Authorization: "sk-mavi-..." },
  });

  if (!response.ok || (response.headers.get("Content-Type") ?? "").includes("application/json")) {
    const err = await response.json();
    throw new Error(err.msg || "Download failed");
  }

  const cd = response.headers.get("Content-Disposition") ?? "";
  const m = cd.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
  const filename = m ? m[1].replace(/['"]/g, "") : "download";

  const blob = await response.blob();
  const a = Object.assign(document.createElement("a"), {
    href: URL.createObjectURL(blob),
    download: filename,
  });
  a.click();
  URL.revokeObjectURL(a.href);
  ```
</CodeGroup>

### Query Parameters

| Parameter | Type   | Required | Description                                                                                      |
| --------- | ------ | -------- | ------------------------------------------------------------------------------------------------ |
| `bucket`  | string | Yes      | GCS bucket holding the file.                                                                     |
| `blob`    | string | Yes      | GCS object path inside `bucket`. Internal slashes are allowed and should not be percent-encoded. |

### Response

**Success (200):** Binary file stream. Response headers include:

* `Content-Type` — MIME type (e.g. `video/mp4`)
* `Content-Disposition: attachment; filename="..."` — original filename
* `Content-Length` — file size in bytes (may be absent under chunked encoding)

**Error:** JSON with `code`, `msg`, `success: false`, `failed: true`. Common cases:

* `Bucket cannot be null or empty` — missing or empty `bucket` query parameter
* `Blob cannot be null or empty` — missing or empty `blob` query parameter
* `Resource does not exist` — the object is not present in the bucket (wrong values, already deleted, or different account namespace)
* `Insufficient balance to download the resource. Please recharge and try again.` — out of credits; nothing is streamed

### Notes

* Use `stream=True` (Python) or `responseType: 'stream'` (axios) to avoid loading the entire file into memory.
* Billing is **size-based** — the deduction happens before the stream is written; failed validation (`Resource does not exist`, etc.) is not billed.
* The `bucket` / `blob` pair becomes invalid when the underlying asset is deleted via [Delete Asset](/visual-intelligence/base/delete).
* For asset-ID-addressed download, see [Download Asset](/visual-intelligence/base/download).
