When an AWS environment has only a few S3 permissions, finding out who can access what is usually straightforward.
That changes when an organization starts using multiple AWS accounts.
A data account may own the S3 buckets, while application accounts, analytics accounts, and other teams consume the data. S3 Access Grants can make this model easier to manage, but it also introduces another question:
How do you find the S3 data that a particular identity can access through Access Grants?
This matters when troubleshooting permissions, building an internal data-access portal, auditing access, or simply answering a developer who asks, "Which S3 locations does this application actually have access to?"
S3 provides the ListCallerAccessGrants API for this purpose. It lets the caller list the Access Grants that give that identity access to S3 data owned by a specified AWS account. The request can also be narrowed to an S3 path.
The important detail is that this is not the same as listing every Access Grant in an AWS account. The API answers a different question:
What S3 Access Grants give me access to this data owner's S3 data?
That distinction becomes important in cross-account environments.
The Problem With Large Multi-Account Environments
Consider an organization with a central data account:
Account 111111111111
|
+-- S3 Access Grants
|
+-- company-data
|
+-- finance/
+-- analytics/
+-- reports/
+-- research/
Several other AWS accounts use this data:
Account 222222222222
|
+-- Reporting application
Account 333333333333
|
+-- Analytics workloads
Account 444444444444
|
+-- Research application
The same application identity may receive access to only a small part of the data.
For example:
ReportingRole
|
+-- READ
s3://company-data/reports/*
AnalyticsRole
|
+-- READ
s3://company-data/analytics/*
ResearchRole
|
+-- READWRITE
s3://company-data/research/*
Now imagine an administrator wants to answer:
Which S3 locations can ReportingRole access?
Looking at the S3 bucket alone does not necessarily give the complete answer when Access Grants are being used.
That is where ListCallerAccessGrants becomes useful.
What ListCallerAccessGrants Actually Does
The API is designed for the identity that is receiving the grant.
The caller can be an IAM identity or an IAM Identity Center corporate directory identity. The request includes the AWS account ID of the S3 data owner. S3 Access Grants then returns the grants that give the caller access to that owner's S3 data.
The basic flow looks like this:
Application Account
|
| IAM Role
v
ReportingRole
|
| ListCallerAccessGrants
|
| Data owner account ID
v
S3 Access Grants
|
v
Accessible S3 locations
The response can contain information such as:
- S3 grant scope
- Permission
- Application ARN, when applicable
- Pagination information
For example, the result could tell the application that it has:
READ
s3://company-data/reports/*
and:
READ
s3://company-data/analytics/monthly/*
The application can then use this information to understand its effective Access Grants coverage.
Why This Is Different From ListAccessGrants
This is an important distinction.
S3 also provides the ListAccessGrants API.
ListAccessGrants lists grants in a particular S3 Access Grants instance. The request includes the account ID of the account that owns that Access Grants instance, and it can be filtered by grantee, permission, grant scope, and other fields.
Think about the two APIs this way:
ListAccessGrants
Question:
"What grants exist in this Access Grants instance?"
versus:
ListCallerAccessGrants
Question:
"What grants give this caller access to the S3 data owned by this account?"
For an application that needs to discover its own accessible data, ListCallerAccessGrants is usually the more natural API.
Cross-Account Access Is a Two-Sided Configuration
There is one part that often causes confusion.
Calling ListCallerAccessGrants does not magically give a role access to another account's Access Grants instance.
The data-owning account first needs to configure cross-account access for its S3 Access Grants instance. AWS documents this as a multi-step configuration:
Data Owner Account
|
+-- S3 Access Grants instance
|
+-- Resource policy
|
+-- Allows consumer account
Then the identity in the consumer account needs the corresponding identity-based permissions.
Finally, the data owner creates the actual grant for the consumer identity.
AWS supports this cross-account model through the Access Grants instance resource policy and the consumer's IAM permissions.
So the architecture is more accurately represented as:
Data Account
|
S3 Access Grants
|
Cross-account policy
|
v
Consumer Account
|
IAM Role
|
v
Application
The API call is only useful after this trust and permission relationship has been configured correctly.
The Permissions Needed for the Caller
The caller needs permission to use ListCallerAccessGrants.
The required IAM action is:
s3:ListCallerAccessGrants
AWS also requires the appropriate cross-account configuration when the S3 data owner is in another account.
A simplified identity policy might look like:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListCallerAccessGrants"
],
"Resource": "*"
}
]
}
In a real production policy, scope the permissions according to your organization's security requirements rather than blindly copying a broad policy.
Also remember that this permission controls the ability to query the caller's grants. It does not mean the role automatically receives access to every S3 object.
The actual data access is still controlled by the grants that exist for that identity.
The Basic AWS CLI Call
The CLI command is straightforward.
For example:
aws s3control list-caller-access-grants \
--account-id 111111111111 \
--region us-east-1
Here:
--account-id
is the AWS account that owns the S3 data and the Access Grants instance.
It is not simply the account where the application is running.
That distinction is easy to miss in a cross-account setup.
For example:
Consumer Account:
222222222222
Data Owner Account:
111111111111
The application in 222222222222 would use:
aws s3control list-caller-access-grants \
--account-id 111111111111 \
--region us-east-1
The result represents the caller's grants against the specified data owner account.
Add an S3 Path Filter
You do not always need every grant.
The API supports an optional grant-scope filter. It must start with s3://, and you can provide a path fragment so S3 Access Grants searches for applicable grants for that path.
For example:
aws s3control list-caller-access-grants \
--account-id 111111111111 \
--region us-east-1 \
--grant-scope "s3://company-data/reports/"
Now the application can ask a much more specific question:
Do I have an Access Grant covering this S3 path?
This can be useful when building an application that displays available datasets.
For example:
User selects:
s3://company-data/reports/
|
v
ListCallerAccessGrants
|
v
Application checks returned grants
|
v
Show permitted data
Reading the Response
A response can contain multiple grants.
For example, conceptually:
{
"CallerAccessGrantsList": [
{
"GrantScope": "s3://company-data/reports/*",
"Permission": "READ",
"ApplicationArn": "ALL"
},
{
"GrantScope": "s3://company-data/analytics/monthly/*",
"Permission": "READ",
"ApplicationArn": "ALL"
}
]
}
The exact response fields and values depend on the configured grants.
The important fields for an application are usually:
GrantScope
Permission
ApplicationArn
GrantScope tells you where the grant applies.
Permission tells you whether the grant provides READ, WRITE, or READWRITE.
ApplicationArn can indicate whether the grant is associated with a specific IAM Identity Center application or applies to ALL applications, depending on the setup.
Using the API From .NET
If your application is written in .NET, the AWS SDK for .NET provides the S3 Control client for these APIs.
A basic call can look like this:
using Amazon.S3Control;
using Amazon.S3Control.Model;
var client = new AmazonS3ControlClient();
var request = new ListCallerAccessGrantsRequest
{
AccountId = "111111111111",
MaxResults = 100
};
var response = await client.ListCallerAccessGrantsAsync(request);
foreach (var grant in response.CallerAccessGrantsList)
{
Console.WriteLine($"Scope: {grant.GrantScope}");
Console.WriteLine($"Permission: {grant.Permission}");
Console.WriteLine($"Application: {grant.ApplicationArn}");
}
The important thing here is that the AWS SDK call uses the credentials of the current caller.
You should not pass another user's access key simply because you want to inspect that user's grants.
The identity making the call is the identity whose grants are being returned.
That makes the API especially useful for applications that need to show users the S3 data they are already authorized to access.
Add a Grant Scope Filter in .NET
For an application that only needs to check a specific S3 path, pass GrantScope.
var request = new ListCallerAccessGrantsRequest
{
AccountId = "111111111111",
GrantScope = "s3://company-data/reports/",
MaxResults = 100
};
var response = await client.ListCallerAccessGrantsAsync(request);
This avoids retrieving unrelated grants when the application only cares about one part of the data hierarchy.
For example, a data browser might have:
Company Data
|
+-- Finance
+-- Analytics
+-- Reports
When the user opens Reports, the application can query grants relevant to that S3 path instead of loading the entire authorization list.
Pagination Matters
One API call does not necessarily mean one HTTP response containing every grant.
The API supports pagination.
If the response contains NextToken, the application needs to make another request using that token to retrieve the next page. The API supports a maximum MaxResults value of 1000 for the request.
A simple .NET pattern is:
var request = new ListCallerAccessGrantsRequest
{
AccountId = "111111111111",
MaxResults = 100
};
do
{
var response = await client.ListCallerAccessGrantsAsync(request);
foreach (var grant in response.CallerAccessGrantsList)
{
Console.WriteLine(
$"{grant.Permission} -> {grant.GrantScope}");
}
request.NextToken = response.NextToken;
} while (!string.IsNullOrEmpty(request.NextToken));
This is important in production.
If an application assumes that the first response contains everything, it may silently miss grants.
That can produce confusing authorization bugs.
Building a Simple Data Access Screen
One practical use for this API is an internal data browser.
Imagine an application with a page called "My S3 Data."
Instead of hard-coding the available locations:
Reports
Analytics
Research
the application can discover the grants dynamically.
The flow could be:
User signs in
|
v
Application receives IAM identity
|
v
ListCallerAccessGrants
|
v
Return accessible S3 locations
|
v
Build data tree
|
v
User selects a dataset
This is close to how AWS describes use cases for the API. AWS specifically notes that ListCallerAccessGrants can be used by applications that show users the S3 data they can access, including data-browsing experiences.
The advantage is that the UI does not need a separate hard-coded authorization table.
The grants become the source of truth for Access Grants-based access.
What If You Need to Audit All Grants?
This is where you should not confuse ListCallerAccessGrants with ListAccessGrants.
Suppose an administrator wants:
Show every grant configured in the Data Account.
That is an account-level administrative question.
ListAccessGrants is designed for listing grants in a specific Access Grants instance. You can filter the results by grantee, permission, grant scope, and application.
For example:
aws s3control list-access-grants \
--account-id 111111111111 \
--region us-east-1 \
--grantee-type IAM \
--grantee-identifier arn:aws:iam::222222222222:role/ReportingRole
This approach is useful when the data owner wants to inspect grants configured for a particular identity.
The difference is important:
Application perspective
-----------------------
"What can I access?"
ListCallerAccessGrants
Data administrator perspective
------------------------------
"What grants have I configured?"
ListAccessGrants
Both APIs answer useful questions, but they solve different problems.
Multiple AWS Accounts Need Multiple Queries
There is another limitation worth understanding.
If an application has access to S3 data owned by several AWS accounts, you should not assume that one ListCallerAccessGrants request automatically returns grants from every possible data owner account.
The API request specifies the AWS account ID of the S3 data owner.
So if your organization has:
Data Account A
Data Account B
Data Account C
your application may need to query each relevant account:
ListCallerAccessGrants
|
+-- Account A
|
+-- Account B
|
+-- Account C
You can then combine the results in your application.
This is an important distinction from the title's "one API call" idea: one API call can discover the caller's grants for a specified data-owner account, but it does not replace the need to query other independent data-owner accounts.
That design keeps account ownership boundaries explicit.
Handling Errors
A production application should expect authorization failures.
For example:
AccessDenied
|
+-- Caller does not have required API permission
or:
AccessDenied
|
+-- Cross-account Access Grants configuration is missing
or:
InvalidRequest
|
+-- Incorrect account ID or grant scope
Do not convert every failure into "No S3 access."
That can hide configuration problems.
There is an important difference between:
No grants returned
and:
The caller was not allowed to query the grants.
Your application should preserve that distinction.
Common Mistakes
Using the Consumer Account ID
In a cross-account scenario, the AccountId parameter identifies the AWS account that owns the S3 Access Grants instance and data being queried.
It is easy to accidentally pass the application account ID.
Assuming One Call Returns Every Account
The API works against the specified data-owner account.
If your application consumes data from multiple independent AWS accounts, query each required account.
Forgetting Pagination
A response may contain NextToken.
Always handle pagination when building production applications.
Using ListAccessGrants for the Wrong Problem
If the application wants to know what the current caller can access, ListCallerAccessGrants is the better fit.
If an administrator wants to inspect grants configured in an Access Grants instance, use ListAccessGrants.
Treating the Grant List as S3 Object Inventory
The API returns access grants and their scopes. It does not return a list of every object inside those S3 locations.
The grant tells you where the caller has authorization; a separate S3 operation is needed when you want to enumerate objects.
When This API Is Useful
ListCallerAccessGrants is particularly useful when S3 permissions are dynamic and application-driven.
Good examples include:
- Internal data browsers
- Multi-account analytics applications
- Self-service data portals
- SaaS applications with tenant-specific S3 access
- Applications using IAM Identity Center
- Debugging cross-account Access Grants
- Displaying accessible datasets to users
- Building authorization-aware S3 navigation
It is less useful if your application already has a fixed list of S3 locations and does not need to discover access dynamically.
Production Checklist
Before using the API in a production application, check:
| Area | What to verify |
| Identity | The API is called using the actual caller identity |
| Account ID | It points to the correct S3 data owner |
| Permissions | s3:ListCallerAccessGrants is allowed |
| Cross-account | Access Grants resource policy is configured |
| Scope | GrantScope is used when narrowing results |
| Pagination | NextToken is handled |
| Errors | Access denied is distinguished from no grants |
| Multiple accounts | Each required data owner is queried |
| Caching | Results are cached only for an appropriate period |
| Security | Grant information is not exposed to unauthorized users |
Summary
S3 Access Grants becomes much easier to work with once you separate two questions: what grants exist, and what grants give the current identity access.
ListCallerAccessGrants is designed for the second question. It lets an IAM identity or supported IAM Identity Center identity discover the S3 data access granted to it by a specified AWS account. It can also filter results by an S3 path.
For developers, this is useful when building applications that need to discover accessible datasets instead of maintaining a separate authorization list.
The important details are easy to miss: the AccountId is the data owner's account, cross-account access must already be configured, pagination must be handled, and multiple independent data-owner accounts require separate queries.
Used this way, the API provides a practical way to connect S3 authorization with applications that need to understand what data the current identity can actually access.
Join the conversation! Your thoughts help the community grow.