ListObjects
ListObjects returns up to 1,000 of the objects in a bucket per request, in ascending order of their keys. A prefix narrows the listing to one part of the bucket, a delimiter groups keys into folder-like common prefixes, and a marker pages through the rest. It lists the current version of each object, so deleted objects and older versions stay out of it - ListObjectVersions shows those.
ListObjects is the original version of the listing API, and Amazon S3 deprecated it in favour of ListObjectsV2, which is the better choice for new code. STRG answers ListObjects as well, so older tools and scripts keep working.
- Each bucket is reached through its own regional endpoint. A bucket in another region answers
404 NoSuchBucketrather than a redirect. - A listing is always signed: an unsigned one is answered
403 AccessDenied, even for a bucket whose Bucket Protection is off. - An access key limited to an object prefix lists by naming that prefix, or a longer one, as
prefix. Ownernames your STRG account.- A query parameter ListObjects does not define is answered with
501 NotImplemented, so a client knows straight away. 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
- Older backup and sync tools. Backup software and file-system mounts such as s3fs that were written before ListObjectsV2 existed use ListObjects to find out what a bucket already holds. They work against STRG as they are.
- Browsing a bucket like folders. With
prefix=photos/anddelimiter=/, a script sees the objects directly insidephotos/and each subfolder as one entry, much as a file manager shows a directory. - Scripts written for Amazon S3 years ago. A nightly job that lists a prefix to decide what to copy or clean up keeps running unchanged once it points at a STRG endpoint.
Request
GET /?prefix=photos/&delimiter=/&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?prefix=photos/&delimiter=/&max-keys=100 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; any other answers 403 AccessDenied. A key limited to an object
prefix lists by setting prefix to that prefix or a longer one, and a listing without it is
answered 403 AccessDenied. Access keys has the details.
URI parameters
| Name | Type | Required | Description |
|---|---|---|---|
Bucket | String | Yes | The bucket to list. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
prefix | String | No | Lists only keys that begin with this string. Needed - as the key's object prefix or a longer one - when the access key is limited to an object prefix. |
delimiter | String | No | Groups keys that contain this string after the prefix into one CommonPrefixes entry each, up to the first occurrence of the delimiter. Usually /. |
marker | String | No | Starts the listing after this key. It can be any key, whether or not it exists. |
max-keys | Integer | No | The most keys and common prefixes to return. At most 1,000, which is also the default. A response may hold fewer. |
encoding-type | String | No | url has keys and prefixes in the response URL-encoded - useful for keys holding characters XML 1.0 cannot carry. |
Any other query parameter, apart from those of a presigned URL, is answered with
501 NotImplemented.
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: STRG has a single storage class and no archived objects, so there is no restore status to return. |
Request body
None.
Response
A successful request answers 200 OK with an XML body.
Response headers
| Name | Description |
|---|---|
x-amz-request-id | The request ID, handy for a support request. |
x-amz-id-2 | The same request ID. |
x-amz-request-charged | Not returned, since STRG has no Requester Pays buckets. |
Response body
A ListBucketResult element holding:
| Element | Description |
|---|---|
Name | The bucket name. |
Prefix | The prefix the request sent. |
Marker | The marker the request sent. |
NextMarker | When the listing is truncated and the request sent a delimiter: the value for marker on the next page. Without a delimiter, the last Key in Contents serves the same purpose. |
MaxKeys | The page size in effect. |
Delimiter | The delimiter the request sent. |
IsTruncated | true if more keys match than this response holds. |
EncodingType | url when the request asked for URL-encoded keys. |
Contents | One per object, described below. |
CommonPrefixes | One per group of keys rolled up by delimiter, each with a Prefix. Each counts as one key against max-keys. |
Each Contents element holds:
| Element | Description |
|---|---|
Key | The object's key. |
LastModified | When the object was stored. |
ETag | The object's entity tag. |
Size | The object's size in bytes. |
StorageClass | Always STANDARD. |
Owner | The object's owner. ID and DisplayName both carry your STRG account ID. |
RestoreStatus | Never returned, as objects are never archived. |
<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>my-bucket</Name>
<Prefix>photos/</Prefix>
<Marker></Marker>
<NextMarker>photos/2024/</NextMarker>
<MaxKeys>2</MaxKeys>
<Delimiter>/</Delimiter>
<IsTruncated>true</IsTruncated>
<Contents>
<Key>photos/cover.jpg</Key>
<LastModified>2026-09-20T08:15:02.000Z</LastModified>
<ETag>"85f30635602dc09bd85957a6e82a2c21"</ETag>
<Size>184312</Size>
<Owner>
<ID>your-account-id</ID>
<DisplayName>your-account-id</DisplayName>
</Owner>
<StorageClass>STANDARD</StorageClass>
</Contents>
<CommonPrefixes>
<Prefix>photos/2024/</Prefix>
</CommonPrefixes>
</ListBucketResult>
Errors
These are the errors a listing can return, and the situation behind each one.
| Code | HTTP status | When |
|---|---|---|
NoSuchBucket | 404 | The bucket does not exist, 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 yet. |
AccessDenied | 403 | The access key lacks List objects, the bucket is outside the key's buckets, or prefix does not start with the key's object prefix; the request is unsigned, an x-amz-* header is not signed, or a presigned URL has expired; or the account is not active. |
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 | The request carries a query parameter ListObjects does not use (presigning's X-Amz-* parameters aside). |
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. |
InternalError | 500 | An unexpected 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 |
|---|---|---|
| Bucket in another region | 301 with x-amz-bucket-region, which the SDKs follow | 404 NoSuchBucket. A bucket is reached through its own regional endpoint, so a client configured with the bucket's region gets its answer first time |
| Bucket owned by another account | 403 AccessDenied | 404 NoSuchBucket |
| Anonymous listing | Allowed where the bucket permits it | Listings are always signed; an unsigned one is 403 AccessDenied. Anonymous reads covers what a public bucket serves |
| Access keys limited to an object prefix | - | prefix starts with the key's object prefix, and a listing outside it is 403 AccessDenied |
| Unknown query parameters | Ignored | 501 NotImplemented, so a client knows straight away |
Owner | Your AWS canonical user ID | Your STRG account ID, in both ID and DisplayName |
| Storage classes | Several | STANDARD for every object; no RestoreStatus |
x-amz-expected-bucket-owner | Checked against the bucket owner | Welcome, with nothing to check |
Examples
Listing a folder
This lists the objects directly under photos/, with each subfolder rolled up into a common
prefix.
- AWS CLI
- Python (boto3)
- JavaScript (AWS SDK v3)
aws s3api list-objects \
--bucket my-bucket \
--prefix photos/ \
--delimiter / \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1
The AWS CLI follows the marker on its own and prints every page as one result.
import boto3
s3 = boto3.client(
"s3",
endpoint_url="https://s3.eu-central-1.strg.com",
region_name="eu-central-1",
)
response = s3.list_objects(Bucket="my-bucket", Prefix="photos/", Delimiter="/")
for folder in response.get("CommonPrefixes", []):
print("folder:", folder["Prefix"])
for obj in response.get("Contents", []):
print(obj["Key"], obj["Size"])
import { ListObjectsCommand, 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 ListObjectsCommand({ Bucket: "my-bucket", Prefix: "photos/", Delimiter: "/" }),
);
for (const folder of response.CommonPrefixes ?? []) {
console.log("folder:", folder.Prefix);
}
for (const object of response.Contents ?? []) {
console.log(object.Key, object.Size);
}
Paging through every object
Each request returns at most 1,000 keys. While IsTruncated is true, the next request sets
marker to NextMarker - or, when there is none, to the last key received.
- AWS CLI
- Python (boto3)
- JavaScript (AWS SDK v3)
aws s3api list-objects \
--bucket my-bucket \
--prefix photos/ \
--query "Contents[].Key" \
--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",
)
marker = ""
while True:
response = s3.list_objects(Bucket="my-bucket", Prefix="photos/", Marker=marker)
contents = response.get("Contents", [])
for obj in contents:
print(obj["Key"])
if not response.get("IsTruncated"):
break
marker = response.get("NextMarker") or contents[-1]["Key"]
import { ListObjectsCommand, S3Client } from "@aws-sdk/client-s3";
const client = new S3Client({
endpoint: "https://s3.eu-central-1.strg.com",
region: "eu-central-1",
});
let marker;
while (true) {
const response = await client.send(
new ListObjectsCommand({ Bucket: "my-bucket", Prefix: "photos/", Marker: marker }),
);
const contents = response.Contents ?? [];
for (const object of contents) {
console.log(object.Key);
}
if (!response.IsTruncated) break;
marker = response.NextMarker ?? contents.at(-1).Key;
}
Related
- ListObjectsV2 - the current listing API, the better choice for new code
- ListObjectVersions - every version and delete marker
- ListMultipartUploads
- HeadObject, GetObject
- Access keys
- S3 API overview - every difference from Amazon S3