← Back to Blog · Published April 30, 2026 · By EKE Services

How to Count All Attachments and ContentDocuments in Your Salesforce Org

Every Salesforce file migration starts with the same question: how many files are we dealing with? The answer drives scope, timeline, cost, and tool choice. But Salesforce makes counting harder than it should be — files live across four different objects, and a naive query can double-count or miss them entirely. This guide gives you the SOQL you actually need, explains what each query is counting (and why), and walks through a worked example for scoping a real migration.

Why count files before you migrate?

Three reasons. First, scope: a 5,000-file migration is a one-day project and a 500,000-file migration is a six-week project. Second, cost: Salesforce file storage is priced per GB, and the bigger the file footprint the bigger the bill — both at the source (where you may be paying for storage you'll abandon) and at the target (where you need capacity to receive it). Third, tooling: under 1,000 files you can probably do it manually; above that you need real tooling with progress tracking, retry logic, and per-file reporting.

Counting also surfaces surprises. The legacy Attachment count is usually a multiple of what people assume, because Attachments aren't visible in the modern Files UI but accumulate quietly on records.

The three file types in Salesforce

Files in Salesforce live in three places:

Migration tools handle each type differently. The tooling section of our ContentVersion vs. Attachment guide covers the differences in detail.

Counting ContentDocuments and ContentVersions

The simplest count — total ContentDocuments in the org:

SELECT COUNT() FROM ContentDocument

That gives you the number of distinct files. Each ContentDocument has at least one ContentVersion, but may have many (one per uploaded version). To count just the latest version of each file:

SELECT COUNT() FROM ContentVersion WHERE IsLatest = true

That number should match the ContentDocument count. If it doesn't, you have either deleted ContentVersions or — more commonly — a few ContentDocuments with no live versions due to an admin's hard-delete operation.

To count all versions including history:

SELECT COUNT() FROM ContentVersion

The difference between the two ContentVersion counts tells you the version-history overhead in your storage.

Counting legacy Attachments

The Attachment object is dead simple — one row per file:

SELECT COUNT() FROM Attachment

To filter by parent object type, use the ParentId prefix. Account IDs start with 001, Contacts with 003, Cases with 500, Opportunities with 006:

SELECT COUNT() FROM Attachment WHERE Parent.Type = 'Account'

Or to get a parent-type breakdown in one query:

SELECT Parent.Type, COUNT(Id)
FROM Attachment
GROUP BY Parent.Type
ORDER BY COUNT(Id) DESC

Counting Notes

Both objects, separately:

SELECT COUNT() FROM Note
SELECT COUNT() FROM ContentNote

The legacy Note object is the older model; ContentNote is the modern one (and is itself a flavor of ContentDocument, so it'll already be counted in your ContentDocument total above). For a true unique count, query Note separately and add it to the ContentDocument count.

Per-object breakdowns

ContentDocumentLink is the join table connecting ContentDocuments to parent records. To count modern files per parent object:

SELECT LinkedEntity.Type, COUNT(Id)
FROM ContentDocumentLink
GROUP BY LinkedEntity.Type
ORDER BY COUNT(Id) DESC

Note that ContentDocumentLink can have multiple rows per ContentDocument (a file shared with three Accounts has three ContentDocumentLink rows). The count above is link count, not unique file count. To dedupe, count ContentDocumentId distinct values — but SOQL doesn't support COUNT_DISTINCT on standard objects. The workaround is to query ContentDocumentLink grouped by ContentDocumentId and count the result rows externally.

For a more complete parent-object breakdown, the SF Count Attachments tool handles dedup automatically — and includes custom objects without needing you to know their key prefixes in advance.

Counting by file size

Counts alone don't tell you what your storage looks like. To total file size on modern files:

SELECT SUM(ContentSize) FROM ContentVersion WHERE IsLatest = true

That returns total bytes. Divide by 1024 * 1024 * 1024 for GB.

For Attachments:

SELECT SUM(BodyLength) FROM Attachment

Together those two numbers tell you your total file storage footprint — useful for both migration planning and storage cost calculation. See our storage cost reduction guide for what to do with that number.

Worked example: scoping a 50K-file org

Imagine you run the queries above and find:

TypeCountTotal size
ContentDocument32,40078 GB
Attachment17,20022 GB
Note (legacy)8,10012 MB
Total files49,600100 GB

And the per-object breakdown:

Parent objectContentDocumentsAttachments
Case14,8009,200
Account8,6004,400
Opportunity5,2002,100
Custom_Project__c3,8001,500

Several scoping decisions fall out:

Gotchas and edge cases

Need a precise inventory without writing all this SOQL?

The SF Count Attachments tool runs the queries above (and several you didn't know you needed) and exports a CSV report broken down by object type and file category — including custom objects with no manual configuration.

See the Tooling

Related reading

Get Started