How to Export Asset Metadata from Brandfolder
Brandfolder, now part of Smartsheet, stores your metadata on assets rather than on the files themselves. Here is how to get it into a spreadsheet for quality assessment.
Brandfolder has a built-in Download as CSV action. Select the assets or sections you want, then download. Brandfolder calls the resulting spreadsheet a metasheet, and it carries asset names, descriptions, tags, and custom fields. For a full library inventory or a repeatable export, use the Brandfolder API.
Brandfolder nests things in a specific order, and knowing it makes the export make sense:
- Organization sits at the top and can hold many Brandfolders.
- Brandfolder holds sections, collections, and assets.
- Section groups assets and controls what type can be uploaded into it.
- Collection groups assets for sharing and access control, without duplicating uploads.
- Asset is the unit that carries metadata: name, description, tags, and custom fields.
- Attachment is the actual file. One asset can hold several attachments, such as multiple versions of the same image.
This distinction matters for your assessment. Your metadata lives on the asset, so an export gives you one row per asset, not one row per file.
Download as CSV (the metasheet)
Brandfolder can export the metadata of selected assets as a CSV without downloading any files. The same file format is used for bulk editing, which is why the documentation calls it a metasheet. For an assessment you only need the download half of that workflow.
- Multi-value custom fields use semicolons, not commas. If you open and re-save the CSV, watch that your spreadsheet tool does not convert them.
- Reserved characters cannot appear in any field:
> < ( ) { } [ ] &" *:. Values containing them can break the file. - Large libraries: Brandfolder does not recommend pushing more than 40,000 rows back through a metasheet at once. That guidance is about re-uploading edits. For a read-only assessment export you are simply reading the file, but it is a useful signal that very large selections are worth splitting.
Brandfolder API (v4)
The API is the reliable way to capture an entire library, because the UI export only ever acts on what you have selected. It is also the only practical option if your organization spans several Brandfolders.
GET /api/v4/brandfolders call lists the ones you can reach.import csv
import requests
API_KEY = "your_api_key" # brandfolder.com/profile#integrations
BRANDFOLDER_ID = "your_brandfolder_id"
BASE = "https://brandfolder.com/api/v4"
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
}
def fetch_assets(brandfolder_id):
"""Page through every asset, keeping the included custom fields and tags."""
assets, included = [], []
page, per = 1, 3000 # 3000 is the documented maximum page size
while True:
resp = requests.get(
f"{BASE}/brandfolders/{brandfolder_id}/assets",
headers=HEADERS,
params={
"page": page,
"per": per,
"include": "custom_fields,tags",
"fields": "created_at,updated_at",
},
)
resp.raise_for_status()
payload = resp.json()
assets.extend(payload.get("data", []))
included.extend(payload.get("included", []))
meta = payload.get("meta", {})
if not meta.get("next_page"):
break
page = meta["next_page"]
return assets, included
def index_included(included):
"""Custom fields and tags arrive in a separate array, keyed by id."""
lookup = {}
for item in included:
lookup[(item.get("type"), item.get("id"))] = item.get("attributes", {})
return lookup
assets, included = fetch_assets(BRANDFOLDER_ID)
lookup = index_included(included)
rows = []
custom_field_names = set()
for asset in assets:
attrs = asset.get("attributes", {})
rels = asset.get("relationships", {})
row = {
"Asset ID": asset.get("id", ""),
"Name": attrs.get("name", ""),
"Description": attrs.get("description", ""),
"Approved": attrs.get("approved", ""),
"Created": attrs.get("created_at", ""),
"Updated": attrs.get("updated_at", ""),
}
tags = []
for ref in rels.get("tags", {}).get("data", []) or []:
tag = lookup.get(("tags", ref.get("id")), {})
if tag.get("name"):
tags.append(tag["name"])
row["Tags"] = "; ".join(tags)
# Custom fields become their own columns, one per key.
for ref in rels.get("custom_fields", {}).get("data", []) or []:
field = lookup.get(("custom_fields", ref.get("id")), {})
key = field.get("key") or field.get("name")
value = field.get("value")
if not key:
continue
custom_field_names.add(key)
row[key] = f"{row[key]}; {value}" if key in row else value
rows.append(row)
columns = [
"Asset ID", "Name", "Description", "Approved",
"Created", "Updated", "Tags",
] + sorted(custom_field_names)
with open("brandfolder_metadata.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=columns, extrasaction="ignore")
writer.writeheader()
for row in rows:
writer.writerow(row)
print(f"Exported {len(rows)} assets to brandfolder_metadata.csv")include=custom_fields,tags puts those records in a separate top-level included array rather than inside each asset. You have to join them back by id, which is what index_included above does. Leave the include parameter off and you get no custom fields at all.key and value on custom field records, with a fallback, because attribute naming varies by resource type. Five minutes of checking saves a re-run over a large library.The Reports area at the organization level produces usage analytics: views, downloads, shares, search terms, and user activity. Useful for understanding which assets get used, but none of the report types carry tags or custom fields, so it will not give you a metadata inventory. Reports also needs Insights enabled on a Premium or Enterprise plan. For metadata, use Method 1 or Method 2.
What metadata can you export?
| Field | Download as CSV | Brandfolder API |
|---|---|---|
| Asset name | ✓ | ✓ |
| Description | ✓ | ✓ |
| Tags | ✓ | ✓ |
| Custom fields (single value) | ✓ | ✓ |
| Custom fields (multi value) | ✓ | ✓ |
| Dependent custom fields | ✓ | ✓ |
| Asset ID | ✕ | ✓ |
| Created and updated dates | ✕ | ✓ |
| Approval status | ✕ | ✓ |
| Section | ✕ | ✓ |
| Collections | ✕ | ✓ |
| Attachments and file details | ✕ | ✓ |
| CDN URLs | ✕ | ✓ |
| Usage and download counts | ✕ | ✕ |
- There is no single export everything button. The CSV download acts on your current selection, so a whole-library inventory means selecting every section, or repeating the export per section. The API has no organization-wide call either. The documented pattern is to list brandfolders, then sections within each, then assets within each section.
- Assets are not files. Metadata sits on the asset while the files sit underneath as attachments, so an asset holding four image versions is still one row. Expect your row count to be lower than your file count.
- Pagination is mandatory on large libraries. API list calls default to 100 records per page and cap at 3,000, so anything sizeable requires looping through pages using the
metablock. - Auto-generated tags are mixed in with human ones. Brandfolder flags tags created by its own image analysis. If your assessment is meant to measure deliberate human curation, it is worth separating those before you draw conclusions.
- Labels may not exist for you. Labels are an optional organizing feature that is not enabled on every account, so do not count on them being in an export.
Brandfolder is a Smartsheet product, and its documentation moved accordingly. Help articles now live under help.smartsheet.com/brandfolder, and the API reference under developers.smartsheet.com/api/brandfolder. Older brandfolder.com documentation links redirect there, so bookmarks should still resolve.
You have your metadata export.
Now score it.
Upload your CSV or Excel file to MQS and get a structural metadata health score out of 100 with dimension breakdowns and actionable diagnostics.