ListObjectVersions
ListObjectVersions lists the versions and delete markers of the objects in a bucket, up to 1,000 a page, optionally narrowed by prefix and grouped by delimiter. Every STRG bucket keeps versions of its objects, so an overwrite or a delete never loses the earlier data - and this listing is where those earlier versions, and the delete markers that hide deleted objects, can be seen. For the current objects alone, ListObjectsV2 is the simpler choice.
- An access key limited to a key prefix names that prefix, or a longer one, as
prefix; a listing without it answers403 AccessDenied. Ownercarries your STRG account ID, in bothIDandDisplayName.x-amz-expected-bucket-owneris welcome and has nothing to check: a key only ever reaches buckets of its own account.x-amz-optional-object-attributesandx-amz-request-payerhave nothing to do here: there is one storage class and no request fees.- Every listing is signed, even of a public bucket: an unsigned one answers
403 AccessDenied.
Every version and delete marker in the listing is billed as stored data until it is permanently deleted or a lifecycle policy removes it.
When you'd use it
- Recovering a deleted file. Someone removed
photos/2024/cat.jpgby mistake. The listing shows its delete marker on top and the earlier version underneath; deleting the marker with DeleteObject brings the photo back exactly as it was. - Going back to an earlier version. A config file was overwritten with a broken one. The
listing gives each version's
VersionIdandLastModified, and GetObject with the rightversionIdreads the good one. - Seeing where storage goes. Old versions are billed as stored data, so a periodic report -
or an agent asked why a bucket is larger than its current files - adds up the
Sizeof the versions that are not latest, before deciding on a lifecycle rule or permanent deletes. - Emptying a bucket before deleting it. A bucket that still holds versions or delete markers cannot be deleted, so a tear-down script lists them all and removes each by version ID with DeleteObjects.
Request
GET /?versions&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?versions on s3.eu-central-1.strg.com, which a bucket
name containing a dot needs - Addressing explains both.
Permissions
The access key needs List objects (objects:list). A key limited to certain buckets lists
those buckets only, and any other answers 403 AccessDenied. A key limited to a key prefix sends
prefix set to that prefix or a longer one. Access keys has
the details.
URI parameters
| Name | Type | Required | Description |
|---|---|---|---|
Bucket | String | Yes | The bucket to list. |
versions | Flag | Yes | Selects the versions listing. Sent with no value. |
prefix | String | No | Lists only keys that begin with this string. For an access key limited to a key prefix, it is that prefix or a longer one. |
delimiter | String | No | Groups keys that share the part between prefix and the first occurrence of the delimiter into one CommonPrefixes entry. Each group counts as one result against max-keys. |
max-keys | Integer | No | The most versions, delete markers and common prefixes to return. Default and maximum 1,000. |
key-marker | String | No | The key the listing starts after. A follow-up request sends the previous page's NextKeyMarker here. |
version-id-marker | String | No | The version of key-marker the listing starts after. A follow-up request sends the previous page's NextVersionIdMarker here. |
encoding-type | String | No | url returns key names URL-encoded, for keys that contain characters XML 1.0 cannot carry. |
A query parameter other than these is answered 501 NotImplemented, so a client knows straight
away; presigning's X-Amz-* parameters and the JavaScript SDK's x-id are always welcome.
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-optional-object-attributes | String | No | Not applicable: there is one storage class and nothing is archived, so there is no RestoreStatus to add. |
x-amz-request-payer | String | No | Not applicable: STRG has no Requester Pays buckets and charges no request fees. |
Request body
None.
Response
A successful request answers 200 OK with the listing as XML.
Response headers
| Name | Description |
|---|---|
x-amz-request-id | The request ID, handy for a support request. |
x-amz-request-charged is never returned.
Response body
A ListVersionsResult document, with Version and DeleteMarker entries listed together.
| Element | Description |
|---|---|
Name | The bucket name. |
Prefix | The prefix sent. |
Delimiter | The delimiter sent. |
MaxKeys | The max-keys in effect. |
EncodingType | url when the request sent encoding-type=url. Key, Prefix, Delimiter, KeyMarker and NextKeyMarker are then URL-encoded. |
KeyMarker | The key-marker sent. |
VersionIdMarker | The version-id-marker sent. |
IsTruncated | true if more results remain. |
NextKeyMarker | When truncated, the value for key-marker on the next page. |
NextVersionIdMarker | When truncated, the value for version-id-marker on the next page. |
Version | One object version: Key, VersionId, IsLatest, LastModified, ETag, Size, StorageClass (always STANDARD) and Owner. |
DeleteMarker | One delete marker: Key, VersionId, IsLatest, LastModified and Owner. A key whose latest entry is a delete marker appears deleted to GetObject and to ListObjectsV2. |
CommonPrefixes | One entry per group of keys rolled up by delimiter, each with a Prefix. |
Owner | Inside Version and DeleteMarker. ID and DisplayName both carry your STRG account ID. |
Version IDs are opaque strings, and work exactly as returned.
<?xml version="1.0" encoding="UTF-8"?>
<ListVersionsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>my-bucket</Name>
<Prefix>photos/</Prefix>
<KeyMarker></KeyMarker>
<VersionIdMarker></VersionIdMarker>
<MaxKeys>100</MaxKeys>
<Delimiter>/</Delimiter>
<IsTruncated>false</IsTruncated>
<DeleteMarker>
<Key>photos/2024/cat.jpg</Key>
<VersionId>EXAMPLE-VERSION-ID-3</VersionId>
<IsLatest>true</IsLatest>
<LastModified>2026-09-20T10:15:00.000Z</LastModified>
<Owner>
<ID>EXAMPLE-ACCOUNT-ID</ID>
<DisplayName>EXAMPLE-ACCOUNT-ID</DisplayName>
</Owner>
</DeleteMarker>
<Version>
<Key>photos/2024/cat.jpg</Key>
<VersionId>EXAMPLE-VERSION-ID-2</VersionId>
<IsLatest>false</IsLatest>
<LastModified>2026-09-12T08:30:00.000Z</LastModified>
<ETag>"85f30635602dc09bd85957a6e82a2c21"</ETag>
<Size>48213</Size>
<StorageClass>STANDARD</StorageClass>
<Owner>
<ID>EXAMPLE-ACCOUNT-ID</ID>
<DisplayName>EXAMPLE-ACCOUNT-ID</DisplayName>
</Owner>
</Version>
<Version>
<Key>photos/2024/cat.jpg</Key>
<VersionId>EXAMPLE-VERSION-ID-1</VersionId>
<IsLatest>false</IsLatest>
<LastModified>2026-09-01T17:02:00.000Z</LastModified>
<ETag>"0b5f4d7e3c2a19f86e1d4c7b2a9f3e10"</ETag>
<Size>45102</Size>
<StorageClass>STANDARD</StorageClass>
<Owner>
<ID>EXAMPLE-ACCOUNT-ID</ID>
<DisplayName>EXAMPLE-ACCOUNT-ID</DisplayName>
</Owner>
</Version>
<CommonPrefixes>
<Prefix>photos/2025/</Prefix>
</CommonPrefixes>
</ListVersionsResult>
Errors
These are the errors a versions listing can return, and the situation behind each one.
| Code | HTTP status | When |
|---|---|---|
NoSuchBucket | 404 | The bucket does not exist, belongs to another account, 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 its buckets, prefix does not start with its key prefix, or the account is not active. Also every unsigned request. |
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 other than those listed under URI parameters (presigning's X-Amz-* parameters and the JavaScript SDK's x-id 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. |
Differences from Amazon S3
The table lists each place where STRG behaves differently, and what that means for a client.
| Aspect | Amazon S3 | STRG |
|---|---|---|
| Versioning | Only buckets with versioning enabled or suspended hold more than one version | Always on. Overwrites and deletes keep earlier versions, so there is always something to go back to - and every version is billed as stored data until it is deleted |
| Bucket in another region | 301 redirect with x-amz-bucket-region | 404 NoSuchBucket. A bucket is reached through its own regional endpoint |
| Nothing stored in the endpoint's region yet | Any valid key reaches the endpoint | 404 NoSuchBucket |
| Access keys limited to a key prefix | - | The listing names the key's prefix, or a longer one, as prefix; without it, 403 AccessDenied |
Owner | Your AWS canonical user ID | Your STRG account ID, in both ID and DisplayName |
StorageClass | Any storage class | Always STANDARD |
x-amz-optional-object-attributes | Returns RestoreStatus | Not applicable: nothing is archived |
x-amz-request-payer, x-amz-request-charged | Requester Pays | Not applicable: there are no request fees |
x-amz-expected-bucket-owner | Checked against the bucket owner | Welcome, with nothing to check |
| Unsigned requests to public buckets | Answered where the bucket allows public listing | 403 AccessDenied: a listing is always signed |
Examples
Listing every version and delete marker under a prefix. The AWS CLI and the boto3 paginator
follow NextKeyMarker and NextVersionIdMarker on their own:
- AWS CLI
- Python (boto3)
- JavaScript (AWS SDK v3)
aws s3api list-object-versions \
--bucket my-bucket \
--prefix photos/2024/ \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1
The versions of one object only, as a table:
aws s3api list-object-versions \
--bucket my-bucket \
--prefix photos/2024/cat.jpg \
--query "Versions[?Key=='photos/2024/cat.jpg'].[VersionId,IsLatest,LastModified,Size]" \
--output table \
--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_object_versions")
for page in paginator.paginate(Bucket="my-bucket", Prefix="photos/2024/"):
for version in page.get("Versions", []):
latest = "latest" if version["IsLatest"] else ""
print(version["Key"], version["VersionId"], version["Size"], latest)
for marker in page.get("DeleteMarkers", []):
latest = "latest" if marker["IsLatest"] else ""
print(marker["Key"], marker["VersionId"], "delete marker", latest)
import { ListObjectVersionsCommand, S3Client } from "@aws-sdk/client-s3";
const client = new S3Client({
endpoint: "https://s3.eu-central-1.strg.com",
region: "eu-central-1",
});
let KeyMarker;
let VersionIdMarker;
do {
const page = await client.send(
new ListObjectVersionsCommand({
Bucket: "my-bucket",
Prefix: "photos/2024/",
KeyMarker,
VersionIdMarker,
}),
);
for (const version of page.Versions ?? []) {
console.log(version.Key, version.VersionId, version.Size, version.IsLatest ? "latest" : "");
}
for (const marker of page.DeleteMarkers ?? []) {
console.log(marker.Key, marker.VersionId, "delete marker", marker.IsLatest ? "latest" : "");
}
KeyMarker = page.IsTruncated ? page.NextKeyMarker : undefined;
VersionIdMarker = page.IsTruncated ? page.NextVersionIdMarker : undefined;
} while (KeyMarker !== undefined);
A deleted object comes back when its latest delete marker is deleted with
DeleteObject and that marker's versionId. An earlier version is read by
passing its versionId to GetObject.
Related
- ListObjectsV2 - the current objects only
- ListObjects
- GetBucketVersioning
- DeleteObject and DeleteObjects
- GetObject and HeadObject
- Lifecycle policies - removing old versions automatically
- Access keys
- S3 API overview - every difference from Amazon S3