aws-sdk-kotlin
You must generate an Access Key before getting started. All examples will utilize access_key_id and access_key_secret variables which represent the Access Key ID and Secret Access Key values you generated.
This example uses the aws-sdk-kotlin ↗. You must pass in the R2 configuration credentials when instantiating your S3 client:
import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProviderimport aws.sdk.kotlin.services.s3.S3Clientimport aws.sdk.kotlin.services.s3.listObjectsimport aws.sdk.kotlin.services.s3.model.GetObjectRequestimport aws.sdk.kotlin.services.s3.model.PutObjectRequestimport aws.sdk.kotlin.services.s3.presigners.presignGetObjectimport aws.sdk.kotlin.services.s3.presigners.presignPutObjectimport aws.smithy.kotlin.runtime.auth.awscredentials.Credentialsimport aws.smithy.kotlin.runtime.net.url.Urlimport kotlinx.coroutines.runBlockingimport kotlin.time.Duration.Companion.minutes
val ACCOUNT_ID = "<ACCOUNT_ID>"val ACCESS_KEY = "<ACCESS_KEY>"val SECRET_KEY = "<SECRET_KEY>"
fun main() = runBlocking { val r2Client = S3Client.fromEnvironment { region = "auto" // Required by SDK, but not used by R2 endpointUrl = Url.parse("https://${ACCOUNT_ID}.r2.cloudflarestorage.com") credentialsProvider = StaticCredentialsProvider( Credentials( accessKeyId = ACCESS_KEY, secretAccessKey = SECRET_KEY ), ) }
println("Available buckets:") r2Client.listBuckets().buckets?.forEach { bucket -> println("* ${bucket.name}") }
val bucketName = "<BUCKET_NAME>" println("\nObjects in bucket '${bucketName}':") r2Client.listObjects { bucket = bucketName }.contents?.forEach { println("* ${it.key} (size: ${it.size} bytes, modified: ${it.lastModified})") }
r2Client.close()}Available buckets:* my-bucket-1* my-bucket-2
Objects in bucket 'my-bucket-1':* image1.png (size: 253167 bytes, modified: 2026-01-17T11:30:58.896Z)* image2.png (size: 247027 bytes, modified: 2026-01-17T11:30:57.779Z)You can also generate presigned links that can be used to temporarily share public read or write access to a bucket.
val uploadUrl = r2Client.presignPutObject( input = PutObjectRequest { bucket = bucketName key = "README.md" }, duration = 15.minutes,).urlprintln(uploadUrl)
val getUrl = r2Client.presignGetObject( input = GetObjectRequest { bucket = bucketName key = "README.md" }, duration = 15.minutes,).urlprintln(getUrl)You can use these presigned URLs with any HTTP client. For example, to upload a file using the PUT URL:
curl -X PUT "https://<your-presigned-put-url>" -H "Content-Type: application/octet-stream" --data-binary "@local-file.txt"To download a file using the GET URL:
curl -X GET "https://<your-presigned-get-url>" -o downloaded-file.txt