Skip to main content

DeleteObjects

DeleteObjects removes up to 1,000 objects from one bucket in a single request, which saves a round trip per object when there is a lot to tidy away. Each entry names a key and, if you like, a version ID. The response reports how every entry went - or, in quiet mode, only the ones that did not go through. For a single object, DeleteObject does the same job.

Every STRG bucket keeps every version of its objects, so what an entry does depends on whether it carries a version ID:

EntryEffect
Key onlyWrites a delete marker. The object disappears from listings and reads, but every earlier version is kept - and billed as stored data - until it is deleted by its version ID or a lifecycle policy removes it
Key and VersionIdPermanently deletes that version. Naming a delete marker's version ID removes the marker, so the previous version becomes current again
Good to know on STRG
  • For an access key limited to an object prefix, every key in the request is checked against the prefix up front, so a request either stays inside the prefix as a whole or deletes nothing.
  • A key with a . or .. path segment (a/../b) cannot be written into a URL, and DeleteObjects is the way to delete one: its keys travel in the request body.
  • Per-object conditions (ETag, LastModifiedTime, Size) and x-amz-mfa are answered with 501 NotImplemented, so a client knows straight away.
  • x-amz-expected-bucket-owner is welcome and has nothing to check: a key only ever reaches buckets of its own account.

When you'd use it​

  • Clearing out a batch of files. A photo app whose user deletes an album can remove all of its pictures in one request rather than one request per picture.
  • Cleaning up after tests. A CI pipeline that writes objects under a run's own prefix can list them at the end and delete them in batches of 1,000, so test data does not pile up as stored data.
  • Freeing up stored data for good. Because every bucket keeps old versions, a clean-up script can list them with ListObjectVersions and delete them by version ID, which is what stops them being billed.
  • An agent tidying up after itself. An AI agent can delete the files it no longer needs - with a human confirming first whenever an entry names a version, unless its key allows it to act without confirmation.

Request​

POST /?delete 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: <SHA-256 of the body>
Content-MD5: <base64 MD5 of the body>
Content-Type: application/xml

<Delete xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Object>
<Key>photos/2024/cat.jpg</Key>
</Object>
<Object>
<Key>photos/2024/dog.jpg</Key>
<VersionId>VersionId</VersionId>
</Object>
<Quiet>false</Quiet>
</Delete>

The path-style form is POST /my-bucket?delete on s3.eu-central-1.strg.com - Addressing explains both forms.

Permissions​

What the access key needs depends on the entries in the request - Access keys describes each permission.

EntryPermission
Key only (writes a delete marker)Write objects (objects:write)
Key and VersionId (permanent delete)Delete object versions (objects:delete-versions)
With x-amz-bypass-governance-retention: trueAlso Bypass governance retention (object-retention:bypass-governance)

A key limited to certain buckets reaches only those buckets. A key limited to an object prefix is checked against every key in the request. STRG checks all the entries before deleting anything, so a single entry the access key may not delete - outside the prefix, or with a version ID when the key does not hold Delete object versions - answers the whole request with 403 AccessDenied, and nothing is deleted. A request is therefore all within the key's reach or left untouched.

URI parameters​

NameTypeRequiredDescription
BucketStringYesThe bucket holding the objects.
deleteFlagYesSelects this operation. Sent without a value.

Request headers​

NameTypeRequiredDescription
Content-MD5StringYes, unless an x-amz-checksum-* header is sentThe base64-encoded MD5 digest of the request body. A request with neither is answered with 400 InvalidRequest. The AWS SDKs and the AWS CLI send an integrity header automatically, so this is taken care of.
x-amz-checksum-crc32, x-amz-checksum-crc32c, x-amz-checksum-crc64nvme, x-amz-checksum-sha1, x-amz-checksum-sha256StringNoA checksum of the request body, accepted in place of Content-MD5.
x-amz-sdk-checksum-algorithmStringNoThe algorithm the SDK used for the checksum header.
x-amz-bypass-governance-retentionBooleanNotrue deletes object versions locked in governance mode. Needs Bypass governance retention. It has no effect on compliance mode - Bypassing governance retention has the details.
x-amz-mfaStringNoSTRG has no MFA delete, and answers 501 NotImplemented.
x-amz-request-payerStringNoHas nothing to do: STRG has no Requester Pays buckets.
x-amz-expected-bucket-ownerStringNoWelcome, and has nothing to check: an access key only ever reaches its own account's buckets.

Request body​

A Delete document of up to 2 MiB (2,097,152 bytes) - room for 1,000 keys of the longest length. A larger body is answered with 400 MaxMessageLengthExceeded.

ElementTypeRequiredDescription
DeleteContainerYesThe root element.
Delete/ObjectContainerYesOne entry per object or version to delete. At least 1, at most 1,000.
Delete/Object/KeyStringYesThe object key. Not empty.
Delete/Object/VersionIdStringNoThe version to delete permanently. Without it, the entry writes a delete marker instead.
Delete/Object/ETagStringNoA conditional delete, which STRG answers with 501 NotImplemented.
Delete/Object/LastModifiedTimeTimestampNoA conditional delete, which STRG answers with 501 NotImplemented.
Delete/Object/SizeLongNoA conditional delete, which STRG answers with 501 NotImplemented.
Delete/QuietBooleanNotrue returns only the entries that failed. Defaults to false, which returns every entry. At most once.

STRG reads the document strictly, so every key is read exactly the way it was meant. Comments, CDATA sections, a DOCTYPE, processing instructions (other than a leading <?xml …?> declaration), namespace prefixes, attributes other than xmlns, unknown elements and entities other than XML's five predefined ones and character references are answered with 400 MalformedXML. The documents the AWS SDKs and the AWS CLI build are all welcome as they are.

Response​

A successful request answers with a DeleteResult document. The request succeeds as a whole even when some entries do not, so the Error elements in the body are where an entry that failed shows up.

Response headers​

NameDescription
x-amz-request-idThe request ID, for support requests.

x-amz-request-charged is never sent, since STRG has no Requester Pays buckets.

Response body​

ElementDescription
DeleteResultThe root element.
DeleteResult/DeletedOne per entry that succeeded. Omitted in quiet mode.
Deleted/KeyThe object key.
Deleted/VersionIdThe version that was deleted, for an entry that named one.
Deleted/DeleteMarkertrue when a delete marker was written, or when the version deleted was itself a delete marker.
Deleted/DeleteMarkerVersionIdThe version ID of the delete marker written, for an entry without a version ID.
DeleteResult/ErrorOne per entry that failed.
Error/KeyThe object key.
Error/VersionIdThe version ID named in the entry, if any.
Error/CodeAn S3 error code, such as AccessDenied.
Error/MessageA description of the error.
<?xml version="1.0" encoding="UTF-8"?>
<DeleteResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Deleted>
<Key>photos/2024/cat.jpg</Key>
<DeleteMarker>true</DeleteMarker>
<DeleteMarkerVersionId>DeleteMarkerVersionId</DeleteMarkerVersionId>
</Deleted>
<Deleted>
<Key>photos/2024/dog.jpg</Key>
<VersionId>VersionId</VersionId>
</Deleted>
</DeleteResult>

Errors​

These errors answer the request as a whole, and nothing in it is deleted:

CodeHTTP statusWhen
MalformedXML400The body is not a valid Delete document, has no Object, more than 1,000, or an empty Key.
MaxMessageLengthExceeded400The body is larger than 2 MiB.
InvalidRequest400Neither Content-MD5 nor an x-amz-checksum-* header was sent.
BadDigest400Content-MD5 does not match the body.
XAmzContentSHA256Mismatch400x-amz-content-sha256 does not match the body.
NotImplemented501The request carries x-amz-mfa, or an entry carries ETag, LastModifiedTime or Size.
AccessDenied403The access key is not valid for the bucket, a key is outside its object prefix, or it lacks a permission an entry needs - see Permissions above.
NoSuchBucket404There is no bucket of that name of yours in the endpoint's region - it does not exist, is in another region, or your account has nothing stored in this endpoint's region yet.
InvalidAccessKeyId403The access key does not exist, has expired or been revoked, or it was created moments ago and is not yet usable in this endpoint's region, where you already store data. It becomes usable shortly after.
SignatureDoesNotMatch403The request signature is wrong.
RequestTimeTooSkewed403The request time is more than 15 minutes from the server's.
AuthorizationHeaderMalformed400The signature's credential scope names a region other than the endpoint's.
SlowDown503Too many requests at once. The AWS CLI and SDKs retry after the Retry-After interval.
ServiceUnavailable503A temporary failure. The AWS CLI and SDKs retry it on their own.

An entry can also fail on its own, while the others go through. It is then reported in an Error element of the response, with its own code and message. A version under retention or a legal hold, for example, stays until the lock ends - Object Lock explains how locks work.

Differences from Amazon S3​

AspectAmazon S3STRG
VersioningDeleting a key in an unversioned bucket removes the objectEvery bucket is versioned: a key without a version ID writes a delete marker, and earlier versions stay billed as stored data
Access key limited to a prefixEvaluated per object by the policyChecked for every key before anything is deleted: a key outside the prefix answers the whole request with 403 AccessDenied
Permissionss3:DeleteObject, s3:DeleteObjectVersion evaluated per objectA version ID in any entry needs Delete object versions, checked up front for the whole request
Keys with . or .. segmentsAddressable by every operationDeleteObjects is the way to delete them, because the key travels in the body rather than the URL
ETag, LastModifiedTime, Size in an entryConditional delete501 NotImplemented
x-amz-mfaMFA delete501 NotImplemented
XML parsingLenientStrict, so every key is read as meant: comments, CDATA, DOCTYPE and namespace prefixes are 400 MalformedXML
Request body-Up to 2 MiB
Bucket in another region301 redirect with x-amz-bucket-region404 NoSuchBucket. The bucket's own regional endpoint reaches it.
x-amz-expected-bucket-ownerChecked against the bucket ownerWelcome, with nothing to check

Examples​

Writing delete markers for two objects:

aws s3api delete-objects \
--bucket my-bucket \
--delete '{"Objects": [{"Key": "photos/2024/cat.jpg"}, {"Key": "photos/2024/dog.jpg"}]}' \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1

Permanently deleting every version of photos/2024/cat.jpg, reporting only the entries that fail. The version IDs come from ListObjectVersions, and the access key needs Delete object versions.

danger

A version deleted by its version ID is gone for good and cannot be restored.

aws s3api list-object-versions \
--bucket my-bucket \
--prefix photos/2024/cat.jpg \
--query '{Objects: [Versions[?Key==`"photos/2024/cat.jpg"`], DeleteMarkers[?Key==`"photos/2024/cat.jpg"`]][][].{Key: Key, VersionId: VersionId}, Quiet: `true`}' \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1 > delete.json

aws s3api delete-objects \
--bucket my-bucket \
--delete file://delete.json \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1
View as Markdown