Salesforce licence and usage audit in 30 minutes: 8 SOQL queries

Eight SOQL queries to find out how many licenses you really use, which objects, Flows and code are still alive, and what you pay for without using. Ready to copy.

By Toni Pérez, co-founder of 2Q Technologies. Salesforce Certified Platform Developer I and Advanced Administrator.Published 3 November 20263 min read

In short

With eight queries, in half an hour you'll know how many users actually log in, how many licenses you have assigned and free, which AppExchange packages you pay for, and how many custom objects, Flows, Apex classes and triggers are in your org. It's the foundation for negotiating a renewal or sizing a migration.

Before you start

You need a user who can view the setup. Run the queries from the Developer Console, in the Query Editor, or from Workbench. The ones marked Tooling API require turning that option on. None of them change anything: they're all read-only.

1. Active users who never log in

SOQL

SELECT Name, Profile.Name, LastLoginDate
FROM User
WHERE IsActive = true
AND (LastLoginDate < LAST_N_DAYS:90 OR LastLoginDate = null)

Each row is a license you pay for that nobody has used in three months.

2. Licenses purchased and used

SOQL

SELECT Name, TotalLicenses, UsedLicenses
FROM UserLicense
WHERE TotalLicenses > 0

Compare the Salesforce license with Salesforce Platform. Many people who only look things up could get by with a cheaper license.

3. Permission set licenses

SOQL

SELECT MasterLabel, TotalLicenses, UsedLicenses
FROM PermissionSetLicense
WHERE TotalLicenses > 0

This is where add-ons assigned per user show up, such as CPQ or AI features.

4. AppExchange packages

SOQL

SELECT NamespacePrefix, Status, AllowedLicenses, UsedLicenses, ExpirationDate
FROM PackageLicense

Each package is another invoice, another vendor and another dependency in a migration.

5. Custom objects

Tooling API

SELECT DeveloperName, NamespacePrefix, CreatedDate
FROM CustomObject
WHERE NamespacePrefix = null

Then count the records in the ones that matter with SELECT COUNT() FROM Objeto__c. An object with no new records in a year is a candidate to be left out.

6. Active Flows and processes

SOQL

SELECT ApiName, ProcessType, LastModifiedDate
FROM FlowDefinitionView
WHERE IsActive = true

The ones with ProcessType = 'Workflow' are Process Builder processes, out of support since the end of 2025.

7. Your own Apex code

Tooling API

SELECT Name, LengthWithoutComments, LastModifiedDate
FROM ApexClass
WHERE NamespacePrefix = null

Long classes nobody has touched in years are debt. The ones that carry your business logic are the first thing to understand, whether you stay or go.

8. Triggers

Tooling API

SELECT Name, TableEnumOrId, Status
FROM ApexTrigger
WHERE NamespacePrefix = null

Several triggers on the same object, plus Flows and rules, is the recipe for those errors nobody can explain.

What to do with the results

  • Right-size your licenses before renewal.
  • List what you pay for and don't use: packages, add-ons and permission set licenses.
  • Flag which objects, Flows and code are still alive. That's the real scope of any change.

If you'd rather not do it yourself, in the first week of a project we run this same audit with a read-only user. We don't change anything in your org.

FAQ

Do I need to be an admin?

You need the View Setup and Configuration permission and, for the Tooling API, API access. Usually that means an admin.

Do these queries change anything in my org?

No. They're read-only queries.

Do they work on Pro Suite?

It depends on your edition's API access. On Enterprise, Unlimited, Core or Advanced, yes.

Sources: SOQL reference (Salesforce Developers), Tooling API guide (Salesforce Developers).