AWS
S3
Boto3
Python
Cloud Storage

check if a key exists in a bucket in s3 using boto3

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Checking whether an object key exists in S3 with boto3 is a routine task in ingestion pipelines, idempotent jobs, and cleanup scripts. The safest method is calling head_object and handling expected exceptions. Correct exception handling is important because S3 errors include both missing-object and permission-related cases.

Core Sections

Use head_object for Existence Checks

head_object requests metadata without downloading content, making it efficient for existence checks.

python
1import boto3
2from botocore.exceptions import ClientError
3
4s3 = boto3.client("s3")
5
6
7def s3_key_exists(bucket: str, key: str) -> bool:
8    try:
9        s3.head_object(Bucket=bucket, Key=key)
10        return True
11    except ClientError as e:
12        code = e.response.get("Error", {}).get("Code")
13        if code in ("404", "NoSuchKey", "NotFound"):
14            return False
15        raise
16
17print(s3_key_exists("my-bucket", "data/file.csv"))

This pattern avoids unnecessary bandwidth usage.

Distinguish Missing Key from Access Problems

A missing key and insufficient permission can both appear as failures. Do not silently treat every error as "missing".

python
1except ClientError as e:
2    code = e.response.get("Error", {}).get("Code")
3    if code in ("404", "NoSuchKey", "NotFound"):
4        return False
5    if code in ("403", "AccessDenied"):
6        raise PermissionError("Access denied for bucket or key")
7    raise

Clear error semantics reduce dangerous false negatives.

Prefix Checks Are Different from Exact Key Checks

If you need to know whether any object exists under a prefix, use list_objects_v2 with MaxKeys=1.

python
def prefix_has_objects(bucket: str, prefix: str) -> bool:
    resp = s3.list_objects_v2(Bucket=bucket, Prefix=prefix, MaxKeys=1)
    return resp.get("KeyCount", 0) > 0

Do not use prefix listing when you require exact object existence.

Reuse Session and Client Configuration

In production jobs, configure retries and region once via boto3 session to avoid repeated setup overhead.

python
1from botocore.config import Config
2
3cfg = Config(retries={"max_attempts": 5, "mode": "standard"})
4s3 = boto3.client("s3", region_name="us-east-1", config=cfg)

Retry behavior improves resilience for transient network errors.

Async and Batch Patterns

For checking many keys, avoid one request at a time in synchronous loops. Use batch design where possible, such as manifest comparisons or S3 inventory files. Excessive head_object calls can become slow and costly at scale.

Testing with moto or Localstack

For unit and integration tests, mock S3 interactions so code paths are deterministic.

python
1# Example test design
2# create bucket
3# upload one key
4# assert existing and missing checks

Reliable tests prevent regressions in exception handling logic.

Bulk Existence Workflows

When checking many keys, repeated head_object calls can become expensive. For large batches, compare expected keys against inventory manifests or paginated listings pulled once per prefix. Choose strategy based on cardinality and freshness requirements.

python
1def existing_keys_in_prefix(bucket: str, prefix: str):
2    paginator = s3.get_paginator("list_objects_v2")
3    for page in paginator.paginate(Bucket=bucket, Prefix=prefix):
4        for item in page.get("Contents", []):
5            yield item["Key"]

Then compute set differences in memory for moderate-sized key sets. This is often cheaper than one call per expected key.

IAM and Cross-account Considerations

In cross-account setups, assume role permissions can differ between listing and head operations. A role may list buckets but not read object metadata. Treat permission modeling as part of design, not only exception handling.

Also include region and endpoint configuration explicitly in multi-region systems so existence checks do not accidentally query the wrong region and produce false missing results.

Use clear metrics for check volume, success rate, and access-denied rate so operational anomalies are visible early.

Common Pitfalls

  • Treating all ClientError responses as missing keys.
  • Using object listing for exact key checks and introducing ambiguity.
  • Ignoring permission errors and masking access problems.
  • Creating new clients repeatedly in tight loops.
  • Running high-volume existence checks without considering request cost.

Summary

  • Use head_object for efficient exact-key existence checks.
  • Handle missing-key and permission errors separately.
  • Use prefix listing only for prefix-level existence questions.
  • Configure retries and clients centrally for production stability.
  • Test error paths so existence logic remains trustworthy.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design