A boto3.Session is the object that owns your credentials and region. There are three ways to build one, and picking the right one is the difference between clean multi-account code and a tangle of environment hacks.
This is a boto3-specific construct, so every example is plain Python.
Build an explicit session when you need a specific profile, region, or credential set instead of whatever the environment happens to provide.
import boto3# Named profile: selects an account defined in ~/.aws/config.session = boto3.Session(profile_name="prod", region_name="us-east-1")s3 = session.client("s3")for bucket in s3.list_buckets()["Buckets"]: print(bucket["Name"])
When to reach for this:
You work across more than one AWS account or role in the same process.
You need to pin a region independently of AWS_REGION.
You are in a multithreaded app and want one isolated session per thread.
You are passing temporary credentials from an STS AssumeRole call.
Default (implicit): you never write Session(). Module-level boto3.client() creates and caches one on boto3.DEFAULT_SESSION, reading AWS_PROFILE, AWS_REGION, and the shared config.
Named profile:boto3.Session(profile_name="...") looks up a [profile NAME] block in ~/.aws/config and matching credentials in ~/.aws/credentials.
Explicit credentials:boto3.Session(aws_access_key_id=..., aws_secret_access_key=..., aws_session_token=...) skips the config files entirely and uses exactly what you pass.
A session resolves credentials lazily through the standard chain: constructor arguments, then environment variables, then shared credentials/config files, then container and instance IAM roles.
profile_name only changes which config section the file-based steps read; it does not disable the rest of the chain.
For assumed roles, prefer letting a profile with role_arn + source_profile do the AssumeRole for you, so boto3 also handles automatic credential refresh.
Passing temporary keys without the session token - assumed-role and STS credentials need aws_session_token; omitting it fails with an auth error. Fix: always pass all three fields for temporary credentials.
Assuming profile_name sets the region - a profile may define a region, but if it does not, you still get NoRegionError. Fix: pass region_name explicitly or set it in the profile.
Sharing one session across threads - sessions are not thread-safe and can produce corrupt or racy credential state. Fix: one session per thread; share only clients.
Hardcoding keys in an explicit session - literal keys leak into source control and do not rotate. Fix: use a named profile or role, and reserve explicit credentials for values you fetched at runtime from STS.
Confusing the default session with no session - module-level calls still use a session; changing AWS_PROFILE after the first call will not retroactively rebuild the cached default session. Fix: set env vars before the first boto3 call, or build an explicit session.
Mixing profiles via env vars in one process - AWS_PROFILE is process-wide, so you cannot serve two accounts by flipping it. Fix: build one explicit session per account instead.
What is the difference between a session and a client?
A session owns credentials, region, and config. A client is created from a session and exposes one method per AWS API operation. You can make many clients from one session.
Do I have to create a Session to use boto3?
No. Module-level boto3.client() uses a cached default session automatically. You create an explicit session only when you need a specific profile, region, or per-thread isolation.
How do I use a named profile?
Pass profile_name="name" to boto3.Session. The name must match a profile in ~/.aws/config (and credentials in ~/.aws/credentials).
When do I need aws_session_token?
Whenever the credentials are temporary, such as those from AssumeRole or GetSessionToken. Long-lived IAM user keys do not use a session token.
Can I use two profiles in one program?
Yes, by creating two explicit sessions with different profile_name values. You cannot do it by flipping AWS_PROFILE, which is process-wide.
Why do I get NoRegionError?
No region was resolvable from the session, the client, AWS_REGION, or the profile. Pass region_name to the session or client.
Should I hardcode credentials in an explicit session?
No. Reserve explicit credentials for values fetched at runtime, like STS output. For static credentials, use a named profile or an IAM role.
Is a session thread-safe?
No. Create one session per thread. Clients created from a session are thread-safe and can be shared.
How do I assume a role cleanly?
Either call sts.assume_role and build an explicit session from the returned credentials, or configure a profile with role_arn and source_profile so boto3 assumes and refreshes for you.
Does changing AWS_PROFILE at runtime switch accounts?
Only for sessions created after the change, and not for the already-cached default session. Set it before your first boto3 call, or use explicit sessions.