Route 53 is DNS, but through the SDK it is really three resources stacked on top of each other: a hosted zone that owns a domain, the record sets inside it that give the actual answers, and the routing policy on those records that decides which answer a resolver gets. Understanding how those three relate - and how you mutate them - is what makes Route 53 automation predictable.
This page is the mental model. The pages that follow turn each piece into working SDK calls.
Route 53 models DNS as three resource layers - hosted zone, record set, and routing policy - and every SDK call touches one of them.
Insight: you never write a single record directly. You submit a change batch to ChangeResourceRecordSets, and Route 53 applies it atomically and asynchronously.
When to Use: any time you create zones, manage records, or shape traffic across regions from code instead of the console.
Limitations/Trade-offs: writes are eventually consistent (PENDING then INSYNC); the API is global and rate-limited; some health-check metrics are pinned to us-east-1.
Related Topics: record types and alias records, routing policies, health checks, and private hosted zones.
A hosted zone is the container for all DNS records of one domain, like example.com. Creating one with CreateHostedZone gives you back a zone id (such as Z1234567890ABC) and, for a public zone, four name servers you delegate to at your registrar. A zone is either public (answers queries from the internet) or private (answers only inside associated VPCs) - a flag set at creation that cannot be flipped later.
Inside a zone live record sets. A record set is one name plus one type plus its answer: www.example.com of type A pointing at an IP, or example.com of type MX listing mail servers. The word "set" matters - a single record set can hold multiple values (several A records for round-robin), and, with routing policies, several record sets can share the same name and type.
# --- Python (boto3) ---import boto3r53 = boto3.client("route53") # global service; region not scopedresp = r53.create_hosted_zone( Name="example.com", CallerReference="2026-07-23-example-zone", # unique idempotency token)print(resp["HostedZone"]["Id"]) # e.g. /hostedzone/Z1234567890ABC
// --- TypeScript (AWS SDK v3) ---import { Route53Client, CreateHostedZoneCommand } from "@aws-sdk/client-route-53";const r53 = new Route53Client({ region: "us-east-1" }); // global endpointconst resp = await r53.send(new CreateHostedZoneCommand({ Name: "example.com", CallerReference: "2026-07-23-example-zone", // unique idempotency token}));console.log(resp.HostedZone?.Id); // e.g. /hostedzone/Z1234567890ABC
The CallerReference is a string you choose that must be unique per call. It is Route 53's idempotency key: retrying the same reference returns an error for an already-created zone rather than silently making a duplicate.
The single most important mechanic is how records change. There is no "update this one record" call. Instead you build a change batch and hand it to ChangeResourceRecordSets. Each change in the batch is an action - CREATE, DELETE, or UPSERT - paired with a full ResourceRecordSet.
CREATE fails if a matching record already exists.
DELETE requires the record's values to match exactly.
UPSERT creates the record if absent or overwrites it if present. It is the workhorse for idempotent automation.
The whole batch is applied atomically: either every change lands or none does. That lets you, for example, delete an old record and create its replacement in one indivisible operation.
The second mechanic is asynchrony. ChangeResourceRecordSets returns immediately with a ChangeInfo whose Status is PENDING. Route 53 then propagates the change across its global fleet, and only when every server has it does the status become INSYNC (typically within 60 seconds). You confirm with GetChange or a waiter rather than assuming the change is live the instant the call returns. This matters when a script creates a record and then immediately depends on resolving it.
The third mechanic is the routing policy, which lets many record sets share one name and type. Each such record carries a SetIdentifier that makes it unique, plus a policy field: Weight for weighted, Region for latency, Failover (PRIMARY / SECONDARY) for failover, or GeoLocation for geolocation. When a resolver asks for the shared name, Route 53 evaluates the policy and returns the winning record. This is the "traffic-shaping" half of the service.
Alias records are a Route 53 specialty with no equivalent in plain DNS. Instead of a value plus TTL, an alias record points at an AWS resource through an AliasTarget carrying that target's canonical hosted zone id, its DNS name, and an EvaluateTargetHealth flag. Aliases let you point the zone apex (example.com with no subdomain) at an ELB, CloudFront distribution, or S3 website - something a CNAME cannot do at the apex - and they resolve for free with no TTL you manage. Record types and aliases get their own page.
Health checks turn traffic shaping into real failover. A health check (CreateHealthCheck) probes an endpoint on an interval; attaching its id to a record via HealthCheckId makes Route 53 stop returning that record when the endpoint is unhealthy. Combined with failover routing, this is DNS-level high availability. One caveat: Route 53 health checks that watch a CloudWatch metric, and several health-check status and metric operations, are anchored in us-east-1, because that is where the global Route 53 control plane and its metrics live.
Global endpoint is the last thing to internalize. Route 53 is not regional. Its API endpoint is global (physically served from us-east-1), so the client does not really have a working "region" the way EC2 does - you still construct the v3 client with region: "us-east-1" to satisfy the SDK, but zones and records are not scoped to a region. Private hosted zones are the exception that proves the rule: they are associated with specific VPCs in specific regions (via VPC = VPCId + VPCRegion), because a private zone only answers inside those networks.
Put together, the model is: create a zone, submit atomic change batches of record sets, optionally give records a routing policy and a health check, and wait for INSYNC before you trust the change. Every later page is an application of that one loop.
"I can update a single record in place." - No. You submit a change batch; use UPSERT to overwrite a record, there is no partial edit call.
"The record is live the moment the call returns." - No. It returns PENDING; wait for INSYNC via GetChange before depending on resolution.
"A CNAME works at the zone apex." - No. Only an alias record can point the apex (example.com) at an AWS resource; a CNAME at the apex is invalid.
"Route 53 has a region like other services." - No, it is global. You pass a region to the v3 client only to satisfy the SDK; zones are not region-scoped.
"A private zone can be made public later." - No. Public vs private is fixed at zone creation and cannot be changed.
Hosted zones (the container for a domain's records), record sets (the individual DNS answers), and routing policies (the rule on a record that decides which answer wins when several share a name).
How do I change a DNS record via the SDK?
Call ChangeResourceRecordSets with a change batch. Each change is an action (CREATE, DELETE, or UPSERT) plus a full ResourceRecordSet. The batch is applied atomically.
What is the difference between CREATE, DELETE, and UPSERT?
CREATE fails if the record exists, DELETE needs an exact match to remove, and UPSERT creates-or-overwrites. UPSERT is the safest for idempotent automation.
What does the CallerReference do?
It is a unique idempotency token you supply to CreateHostedZone and CreateHealthCheck. Reusing a reference that already created a resource returns an error instead of making a duplicate.
Why is my change PENDING and not live yet?
Route 53 applies changes asynchronously across its global fleet. The status is PENDING until every server has the change, then INSYNC. Confirm with GetChange or a waiter.
How do I wait for a change to propagate?
Poll GetChange until ChangeInfo.Status is INSYNC, or use the built-in waiter (resource_record_sets_changed in boto3, waitUntilResourceRecordSetsChanged in SDK v3).
What makes an alias record special?
It points at an AWS resource via an AliasTarget (the target's canonical hosted zone id, DNS name, and an EvaluateTargetHealth flag) instead of a plain value, and it can be used at the zone apex where a CNAME cannot.
Does Route 53 have a region?
No, it is a global service with a single global API endpoint served from us-east-1. You still pass region: "us-east-1" to the v3 client, but zones and records are not region-scoped.
Why do some health-check operations require us-east-1?
Route 53's global control plane and its health-check metrics live in us-east-1, so CloudWatch-metric health checks and several status and metric operations reference that region.