ListObjectsV2
ListObjectsV2 lists the objects in a bucket, sorted by key, up to 1,000 per request. prefix
narrows the list to keys starting with some text, delimiter groups keys into folder-like
CommonPrefixes, and continuation-token fetches the next page. It is the listing the AWS SDKs
and the AWS CLI use by default - aws s3 ls calls it too. Every version and delete marker is
what ListObjectVersions shows, and ListObjects
is the older form of this operation.
- An access key limited to an object prefix sends that prefix, or a longer one, as
prefix. A listing without it answers403 AccessDenied. - Each bucket is reached through its own regional endpoint, so a bucket in another region
answers
404 NoSuchBucketrather than a redirect. - With
fetch-owner=true, each object'sOwnernames your STRG account. - Listings are always signed. An unsigned listing answers
403 AccessDenied, even for a bucket whose Bucket Protection is off - anonymous visitors read its objects through GetObject instead. x-amz-expected-bucket-owneris welcome and has nothing to check: a key only ever reaches buckets of its own account.
When you'd use it
- Browsing a folder. A file manager, a sync tool or the AWS CLI shows one "folder" at a time
by listing with
prefix=photos/2024/anddelimiter=/: the objects in it come back asContents, the folders below it asCommonPrefixes. - Walking a whole bucket. A nightly backup or an audit script reads every key, page by page,
following
NextContinuationTokenuntilIsTruncatedisfalse- the SDKs' paginators do this for you. - Checking what is already there. A sync tool compares each key's
Size,ETagandLastModifiedwith its local copy, so only what changed is uploaded again. - An agent finding its own files. An agent keeping its memory in a bucket, with a key limited
to the prefix
agent/, lists withprefix=agent/to see what it stored earlier.
Request
GET /?list-type=2&prefix=photos%2F2024%2F&delimiter=%2F&max-keys=100 HTTP/1.1
Host: my-bucket.s3.eu-central-1.strg.com
Authorization: AWS4-HMAC-SHA256 Credential=...
x-amz-date: 20260926T120000Z
x-amz-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
The path-style form is GET /my-bucket?list-type=2&... on s3.eu-central-1.strg.com, which is
what a bucket name containing a dot needs - Addressing explains
both.
Permissions
The access key needs List objects (objects:list). For a key limited to certain buckets, the
bucket needs to be one of them. For a key limited to an object prefix, the request's prefix
starts with that prefix - a key limited to reports/ can list with prefix=reports/ or
prefix=reports/2026/, and a listing with no prefix or with prefix=re answers
403 AccessDenied. Access keys has the details.
URI parameters
| Name | Type | Required | Description |
|---|---|---|---|
Bucket | String | Yes | The bucket to list. |
list-type | String | Yes | Always 2. Any other value is answered with 501 NotImplemented. |
prefix | String | No | Only keys starting with this text are listed. For an access key limited to an object prefix, it is required and at least as long as the key's own prefix. |
delimiter | String | No | Keys containing this string after the prefix are rolled up into one CommonPrefixes entry per distinct part, up to and including the delimiter - with /, one entry per "folder". Each entry counts as one key towards max-keys. |
max-keys | Integer | No | The most keys and common prefixes to return in one page. Default and maximum 1,000. |
continuation-token | String | No | The NextContinuationToken of the previous page, which fetches the next one. It is an opaque string, best passed along exactly as received. |
start-after | String | No | Lists only keys that sort after this key. |
fetch-owner | Boolean | No | true adds an Owner to each object. The owner is always your STRG account. |
encoding-type | String | No | url returns Key, Prefix, Delimiter and StartAfter URL-encoded, for keys containing characters XML 1.0 cannot carry. |
Any query parameter not listed here - apart from a presigned URL's X-Amz-* parameters and the
JavaScript SDK's x-id - is answered with 501 NotImplemented, so a client knows straight away.
Request headers
| Name | Type | Required | Description |
|---|---|---|---|
x-amz-expected-bucket-owner | String | No | Welcome, with nothing to check: an access key only ever reaches its own account's buckets. |
x-amz-request-payer | String | No | Has no effect: STRG has no Requester Pays buckets. |
x-amz-optional-object-attributes | String | No | Has nothing to add: objects are never archived, so there is no RestoreStatus. |
Request body
None.
Response
A successful request answers 200 OK with a ListBucketResult XML document.
Response headers
| Name | Description |
|---|---|
x-amz-request-id | The request ID, handy for a support request. |
x-amz-request-charged | Never sent, as there are no requester charges. |
Response body
| Element | Description |
|---|---|
Name | The bucket name. |
Prefix | The prefix the request sent. |
Delimiter | The delimiter the request sent, if any. |
StartAfter | The start-after the request sent, if any. |
ContinuationToken | The continuation-token the request sent, if any. |
MaxKeys | The page size in effect. |
KeyCount | How many keys and common prefixes this page holds. Never more than MaxKeys. |
IsTruncated | true if there are more results, which NextContinuationToken fetches. |
NextContinuationToken | Sent when IsTruncated is true, to pass as continuation-token in the next request. |
EncodingType | url, when the request sent encoding-type=url. |
Contents | One per object: Key, LastModified, ETag, Size, StorageClass and, with fetch-owner=true, Owner. |
Contents / Owner | ID and DisplayName both carry your STRG account ID. |
CommonPrefixes | One per rolled-up group when the request sent a delimiter, each with its Prefix. |
Multipart uploads that were started but never completed are not part of this list - ListMultipartUploads shows those.
<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>my-bucket</Name>
<Prefix>photos/2024/</Prefix>
<Delimiter>/</Delimiter>
<MaxKeys>100</MaxKeys>
<KeyCount>3</KeyCount>
<IsTruncated>false</IsTruncated>
<Contents>
<Key>photos/2024/cat.jpg</Key>
<LastModified>2026-09-20T09:14:02.000Z</LastModified>
<ETag>"85f30635602dc09bd85957a6e82a2c21"</ETag>
<Size>482133</Size>
<StorageClass>STANDARD</StorageClass>
</Contents>
<Contents>
<Key>photos/2024/dog.jpg</Key>
<LastModified>2026-09-21T16:40:55.000Z</LastModified>
<ETag>"3729bf569924c845dfe90bfe6281a9ff-2"</ETag>
<Size>104857600</Size>
<StorageClass>STANDARD</StorageClass>
</Contents>
<CommonPrefixes>
<Prefix>photos/2024/holiday/</Prefix>
</CommonPrefixes>
</ListBucketResult>
Errors
These are the errors this request can return, and the situation behind each one.
| Code | HTTP status | When |
|---|---|---|
NoSuchBucket | 404 | The bucket does not exist or belongs to another account, it is in a region other than the endpoint's, or the account has no storage in this endpoint's region. |
AccessDenied | 403 | The access key lacks List objects, the bucket is outside the key's buckets, prefix does not start with the key's object prefix, the account is not active, the request is unsigned, an x-amz-* header is not signed, or a presigned URL has expired. |
InvalidAccessKeyId | 403 | The access key does not exist, has expired or been revoked - or it was created moments ago and is not usable yet in a region where the account already stores data, in which case the same request works shortly afterwards. |
SignatureDoesNotMatch | 403 | The request signature does not match. |
RequestTimeTooSkewed | 403 | The request time is more than 15 minutes from the server's. |
AuthorizationHeaderMalformed | 400 | The signature's credential scope names a region other than the endpoint's. |
InvalidBucketName | 400 | The bucket name is not a valid STRG bucket name - names are 3-58 characters. |
NotImplemented | 501 | list-type is not 2, or the request carries a query parameter this operation does not use. |
SlowDown | 503 | Too many requests. The Retry-After header says when a retry is welcome; the AWS SDKs retry on their own. |
ServiceUnavailable | 503 | A temporary failure; the same request can be retried. |
Differences from Amazon S3
The table lists each place where STRG behaves differently, and what that means for a client.
| Aspect | Amazon S3 | STRG |
|---|---|---|
| Access key limited to a prefix | IAM policy conditions decide | The request sets prefix to the key's prefix or a longer one; without it, 403 AccessDenied |
| Bucket in another region | 301 redirect with x-amz-bucket-region | 404 NoSuchBucket. A bucket is reached through its own regional endpoint. |
Owner with fetch-owner=true | The bucket owner's canonical ID | Your STRG account ID, in both ID and DisplayName |
| Unsigned requests to public buckets | Accepted where the bucket allows public listing | 403 AccessDenied: listings are always signed, and anonymous visitors read objects with GetObject |
x-amz-expected-bucket-owner | Checked against the bucket owner | Welcome, with nothing to check |
x-amz-optional-object-attributes, x-amz-request-payer | Add RestoreStatus; acknowledge requester charges | Nothing to add: objects are never archived, and there are no requester charges |
Examples
Listing a folder
- AWS CLI
- Python (boto3)
- JavaScript (AWS SDK v3)
aws s3api list-objects-v2 \
--bucket my-bucket \
--prefix photos/2024/ \
--delimiter / \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1
The AWS CLI follows NextContinuationToken for you and prints every page as one result.
--max-items 100 stops it after 100 keys.
import boto3
s3 = boto3.client(
"s3",
endpoint_url="https://s3.eu-central-1.strg.com",
region_name="eu-central-1",
)
response = s3.list_objects_v2(
Bucket="my-bucket",
Prefix="photos/2024/",
Delimiter="/",
)
for folder in response.get("CommonPrefixes", []):
print("folder:", folder["Prefix"])
for obj in response.get("Contents", []):
print(obj["Key"], obj["Size"])
import { ListObjectsV2Command, S3Client } from "@aws-sdk/client-s3";
const client = new S3Client({
endpoint: "https://s3.eu-central-1.strg.com",
region: "eu-central-1",
});
const response = await client.send(
new ListObjectsV2Command({
Bucket: "my-bucket",
Prefix: "photos/2024/",
Delimiter: "/",
}),
);
for (const folder of response.CommonPrefixes ?? []) console.log("folder:", folder.Prefix);
for (const object of response.Contents ?? []) console.log(object.Key, object.Size);
Listing every object, page by page
A bucket with more than 1,000 objects takes several requests. The SDKs' paginators follow
NextContinuationToken until IsTruncated is false, so the code only sees one page after
another.
- AWS CLI
- Python (boto3)
- JavaScript (AWS SDK v3)
aws s3api list-objects-v2 \
--bucket my-bucket \
--query 'Contents[].[Key, Size]' \
--output text \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1
import boto3
s3 = boto3.client(
"s3",
endpoint_url="https://s3.eu-central-1.strg.com",
region_name="eu-central-1",
)
paginator = s3.get_paginator("list_objects_v2")
for page in paginator.paginate(Bucket="my-bucket"):
for obj in page.get("Contents", []):
print(obj["Key"], obj["Size"])
import { S3Client, paginateListObjectsV2 } from "@aws-sdk/client-s3";
const client = new S3Client({
endpoint: "https://s3.eu-central-1.strg.com",
region: "eu-central-1",
});
for await (const page of paginateListObjectsV2({ client }, { Bucket: "my-bucket" })) {
for (const object of page.Contents ?? []) console.log(object.Key, object.Size);
}
Listing with a key limited to a prefix
An access key limited to the object prefix reports/ passes it, or a longer one, as the prefix.
The same command without --prefix answers AccessDenied.
- AWS CLI
- Python (boto3)
- JavaScript (AWS SDK v3)
aws s3api list-objects-v2 \
--bucket my-bucket \
--prefix reports/ \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1
import boto3
s3 = boto3.client(
"s3",
endpoint_url="https://s3.eu-central-1.strg.com",
region_name="eu-central-1",
)
paginator = s3.get_paginator("list_objects_v2")
for page in paginator.paginate(Bucket="my-bucket", Prefix="reports/"):
for obj in page.get("Contents", []):
print(obj["Key"])
import { S3Client, paginateListObjectsV2 } from "@aws-sdk/client-s3";
const client = new S3Client({
endpoint: "https://s3.eu-central-1.strg.com",
region: "eu-central-1",
});
for await (const page of paginateListObjectsV2({ client }, { Bucket: "my-bucket", Prefix: "reports/" })) {
for (const object of page.Contents ?? []) console.log(object.Key);
}
Related
- ListObjectVersions - every version and delete marker
- ListObjects - the older listing
- ListMultipartUploads - uploads started but not completed
- HeadObject and GetObject
- Access keys - bucket and prefix limits
- S3 API overview - every difference from Amazon S3