I tried getting Amazon RDS on-demand pricing using the aws pricing command
This page has been translated by machine translation. View original
Good evening, this is Chiba (Kō).
Recently, I've been interested in Amazon RDS Reserved Instances. The other day, I tried using the AWS CLI to bulk-retrieve the "effective hourly rate" for specific region/DB engine combinations. (From that, the main blog post became "checking available combinations of terms and payment options for purchasing," but...)
Since I'm at it, I also want to bulk-retrieve the "effective cost savings rate compared to On-Demand pricing."
To do that, in addition to the "effective hourly rate for Reserved Instances" I already know how to retrieve, I also need to retrieve the "hourly On-Demand rate."
This can be retrieved using the AWS Price List Query API, but when I looked into it, it turned out to be quite complex. This time, I'll introduce how to use the aws pricing command — the AWS CLI command corresponding to the AWS Price List Query API — along with tips for retrieving On-Demand pricing for various DB engines.
(That alone turned into quite a lot, so comparing the retrieved On-Demand pricing with the effective hourly rate of Reserved Instances to calculate the cost savings rate will be saved for another time.)
Summary First
- You can retrieve Amazon RDS On-Demand pricing with
aws pricing get-attribute-values- Reserved Instance pricing can also be retrieved (not retrieved in this blog post)
- There are several attributes to consider when running
aws pricing get-attribute-values. These attributes differ depending on the DB engine
Tested Environment
This has been verified in the following environment.
- macOS
- Zsh version:
zsh 5.9 (arm64-apple-darwin25.0) - AWS CLI version:
aws-cli/2.36.19 Python/3.14.6 Darwin/25.5.0 exe/arm64 - Jq version:
jq-1.6
Commands Used in This Blog Post
In this blog post, I'll use three subcommands of aws pricing that correspond to the AWS Price List Query API.
- describe-services — AWS CLI 2.37.4 Command Reference: Check attribute names that can be specified in filters
- get-attribute-values — AWS CLI 2.37.4 Command Reference: Check candidate values that can be specified per attribute
- get-products — AWS CLI 2.37.4 Command Reference: Retrieve pricing by specifying conditions
Also, all commands share the following options.
--service-code AmazonRDS: Specifies the target service--region us-east-1: Specifies the Pricing API endpoint--output json: Outputs in JSON format for subsequent jq processing
--region refers to the API endpoint and is fixed to us-east-1. When specifying the region whose pricing you want to look up, specify it as the regionCode attribute.
Checking Available Filter Attributes and Values for Amazon RDS
When retrieving On-Demand pricing, you'll ultimately specify various attributes in the --filters of aws pricing get-products to narrow down results. First, let's check what attributes are available and what values can be specified.
The list of attribute names that can be specified in filters is retrieved with describe-services. Specify Amazon RDS as the service code and check the list.
aws pricing describe-services \
--service-code AmazonRDS \
--region us-east-1 \
--output json \
| jq -r '.Services[0].AttributeNames[]' | sort -f
The results as of September 2026 are as follows.
acu
clockSpeed
currentGeneration
databaseEdition
databaseEngine
dedicatedEbsThroughput
deploymentModel
deploymentOption
engineCode
engineMajorVersion
engineMediaType
enhancedNetworkingSupported
extendedSupportPricingYear
group
groupDescription
instanceFamily
instanceType
instanceTypeFamily
LeaseContractLength
licenseModel
limitlesspreview
location
locationType
maxVolumeSize
memory
minVolumeSize
networkPerformance
normalizationSizeFactor
OfferingClass
operation
physicalProcessor
processorArchitecture
processorFeatures
productFamily
PurchaseOption
regionCode
Restriction
servicecode
servicename
storage
storageMedia
termType
unbundledLicensing
usagetype
vcpu
volumeName
volumeType
windowslicensemultiplier
Version without jq processing
% aws pricing describe-services \
--service-code AmazonRDS \
--region us-east-1 \
--output json
{
"Services": [
{
"ServiceCode": "AmazonRDS",
"AttributeNames": [
"termType",
"productFamily",
"servicecode",
"location",
"locationType",
"instanceType",
"currentGeneration",
"instanceFamily",
"vcpu",
"physicalProcessor",
"memory",
"storage",
"networkPerformance",
"processorArchitecture",
"engineCode",
"databaseEngine",
"licenseModel",
"deploymentOption",
"usagetype",
"operation",
"instanceTypeFamily",
"normalizationSizeFactor",
"regionCode",
"servicename",
"unbundledLicensing",
"LeaseContractLength",
"PurchaseOption",
"OfferingClass",
"databaseEdition",
"windowslicensemultiplier",
"group",
"groupDescription",
"deploymentModel",
"clockSpeed",
"processorFeatures",
"enhancedNetworkingSupported",
"engineMediaType",
"storageMedia",
"volumeType",
"minVolumeSize",
"maxVolumeSize",
"dedicatedEbsThroughput",
"volumeName",
"engineMajorVersion",
"extendedSupportPricingYear",
"acu",
"Restriction",
"limitlesspreview"
]
}
],
"FormatVersion": "aws_v1"
}
Then, the candidate values that can be specified for a specific attribute are retrieved with get-attribute-values. Below is an example specifying the databaseEngine attribute.
aws pricing get-attribute-values \
--service-code AmazonRDS \
--attribute-name databaseEngine \
--region us-east-1 \
--output json \
| jq -r '.AttributeValues[].Value'
The results are as follows.
Any
Aurora MySQL
Aurora PostgreSQL
Db2
MariaDB
MySQL (on-premise for Outpost)
MySQL
Oracle (on-premises for Outposts)
Oracle
PostgreSQL (on-premise for Outpost)
PostgreSQL
SQL Server (on-premise for Outpost)
SQL Server
Version without jq processing
% aws pricing get-attribute-values \
--service-code AmazonRDS \
--attribute-name databaseEngine \
--region us-east-1 \
--output json
{
"AttributeValues": [
{
"Value": "Any"
},
{
"Value": "Aurora MySQL"
},
{
"Value": "Aurora PostgreSQL"
},
{
"Value": "Db2"
},
{
"Value": "MariaDB"
},
{
"Value": "MySQL (on-premise for Outpost)"
},
{
"Value": "MySQL"
},
{
"Value": "Oracle (on-premises for Outposts)"
},
{
"Value": "Oracle"
},
{
"Value": "PostgreSQL (on-premise for Outpost)"
},
{
"Value": "PostgreSQL"
},
{
"Value": "SQL Server (on-premise for Outpost)"
},
{
"Value": "SQL Server"
}
]
}
Now that I had a list of DB engine attribute values, I initially hoped that filtering by just the region and DB engine would give me the On-Demand pricing I wanted, but it wasn't that simple.
I also need to understand other attributes. Let me combine the two commands to list the main values for all attributes.
{
echo "| Attribute | Count | Candidate Values |"
echo "| --- | --- | --- |"
for attr in $(aws pricing describe-services \
--service-code AmazonRDS \
--region us-east-1 \
--output json \
| jq -r '.Services[0].AttributeNames[]' | sort -f); do
aws pricing get-attribute-values \
--service-code AmazonRDS \
--attribute-name "$attr" \
--region us-east-1 \
--output json \
| jq -r --arg attr "$attr" '
[.AttributeValues[].Value] as $v
| ($v | length) as $n
| if $n > 15
then "| `\($attr)` | \($n) | \($v[0:4] | join(" / ")) …etc |"
else "| `\($attr)` | \($n) | \($v | join(" / ")) |"
end
'
done
}
The execution looks like this. Output is produced one line at a time.
| Attribute | Count | Candidate Values |
| --- | --- | --- |
| `acu` | 1 | 1 |
| `clockSpeed` | 5 | 2.3 GHz / 2.4 GHz / 2.5 GHz / Up to 3.1 GHz / Up to 3.3 GHz |
| `currentGeneration` | 2 | No / Yes |
| `databaseEdition` | 14 | Advanced / Any / Community / Developer / Enterprise Developer / Enterprise-BYOM / Enterprise / Express / Standard Developer / Standard One / Standard Two / Standard-BYOM / Standard / Web |
| `databaseEngine` | 13 | Any / Aurora MySQL / Aurora PostgreSQL / Db2 / MariaDB / MySQL (on-premise for Outpost) / MySQL / Oracle (on-premises for Outposts) / Oracle / PostgreSQL (on-premise for Outpost) / PostgreSQL / SQL Server (on-premise for Outpost) / SQL Server |
| `dedicatedEbsThroughput` | 27 | 1000 Mbps / 10000 Mbps / 12000 Mbps / 1250 Mbps …etc |
| `deploymentModel` | 1 | Custom |
| `deploymentOption` | 5 | Multi-AZ (SQL Server Mirror) / Multi-AZ (readable standbys) / Multi-AZ / Single-AZ (Oracle RAC) / Single-AZ |
| `engineCode` | 43 | 0 / 10 / 11 / 12 …etc |
| `engineMajorVersion` | 8 | 10 / 11 / 12 / 13 / 14 / 15 / 5.7 / 8.0 |
| `engineMediaType` | 2 | AWS-provided / Customer-provided |
| `enhancedNetworkingSupported` | 1 | Yes |
| `extendedSupportPricingYear` | 4 | Year 1, Year 2 (PP) / Year 1, Year 2 / Year 3 (PP) / Year 3 |
……abbreviated below
Since some attributes have over 20,000 values, I used an approach that truncates output when the count exceeds 15.
Below are the results as of September 2026, with notes added by the author.
| Attribute | Count | Candidate Values | Notes |
|---|---|---|---|
acu |
1 | 1 | Aurora Capacity Unit. The capacity unit for Aurora Serverless |
clockSpeed |
6 | 2.3 GHz / 2.4 GHz / 2.5 GHz / Up to 3.0 GHz / Up to 3.1 GHz / Up to 3.3 GHz | CPU clock frequency |
currentGeneration |
2 | No / Yes | Whether it's a current-generation instance. db.m1 series etc. are No |
databaseEdition |
14 | Advanced / Any / Community / Developer / Enterprise Developer / Enterprise-BYOM / Enterprise / Express / Standard Developer / Standard One / Standard Two / Standard-BYOM / Standard / Web | DB engine edition. Required to uniquely narrow down pricing for Oracle/SQL Server/Db2. Those with -BYOM are for RDS Custom |
databaseEngine |
13 | Any / Aurora MySQL / Aurora PostgreSQL / Db2 / MariaDB / MySQL (on-premise for Outpost) / MySQL / Oracle (on-premises for Outposts) / Oracle / PostgreSQL (on-premise for Outpost) / PostgreSQL / SQL Server (on-premise for Outpost) / SQL Server | DB engine type. Those with (on-premise for Outpost) are for AWS Outposts |
dedicatedEbsThroughput |
27 | 1000 Mbps / 10000 Mbps / 12000 Mbps / 1250 Mbps …etc | Dedicated EBS bandwidth. Only exists up to db.m5/db.r5b generation |
deploymentModel |
1 | Custom | Only present in RDS Custom records |
deploymentOption |
5 | Multi-AZ (SQL Server Mirror) / Multi-AZ (readable standbys) / Multi-AZ / Single-AZ (Oracle RAC) / Single-AZ | AZ placement configuration. Those with (SQL Server Mirror) and (Oracle RAC) are storage-related charges only. |
engineCode |
43 | 0 / 10 / 11 / 12 …etc | Internal code identifying "engine × edition × license type". Described later |
engineMajorVersion |
8 | 10 / 11 / 12 / 13 / 14 / 15 / 5.7 / 8.0 | Engine major version. Used in RDS Extended Support pricing records |
engineMediaType |
2 | AWS-provided / Customer-provided | Engine media provider. RDS Custom (BYOM) is Customer-provided |
enhancedNetworkingSupported |
1 | Yes | Whether enhanced networking is supported |
extendedSupportPricingYear |
4 | Year 1, Year 2 (PP) / Year 1, Year 2 / Year 3 (PP) / Year 3 | RDS Extended Support year |
group |
11 | API Request / Aurora Backtrack / Aurora Global Database / Aurora I/O Operation / Provisioned GB / RDS CDC Data Transfer / RDS I/O Operation / RDS Snapshot Export / RDS Zero-ETL Data Transfer / RDS-PIOPS / RDS-Throughput | Billing item groups other than instance hours |
groupDescription |
12 | Aurora Backtrack Change Records / Aurora Global Database Replicated IO / Data API requests / Input/Output Operation / Performance Insights API requests / RDS Provisioned GP3 IOPS / RDS Provisioned IO2 IOPS / RDS Provisioned IOPS / RDS Provisioned Throughput / RDS Snapshot Size / RDS Zero-ETL Initial Load and Re-sync Export for Commercial Engines / RDS zero-ETL change data capture records consumed from source RDS engine | Description of group |
instanceFamily |
6 | Compute optimized / General purpose / Memory optimized / Micro instances / T3 / T4G | Use-case classification of instance family. T3/T4G have inconsistent naming rather than classification names |
instanceType |
417 | db.c6gd.12xlarge / db.c6gd.16xlarge / db.c6gd.2xlarge / db.c6gd.4xlarge …etc | DB instance class |
instanceTypeFamily |
49 | C6gd / M1 / M2 / M3 …etc | DB instance class type |
LeaseContractLength |
2 | 1yr / 3yr | RI contract term. Used in records with termType=Reserved |
licenseModel |
6 | Bring your own license / Bring your own media / License included / Marketplace / NA / No license required | License type. Required to uniquely narrow down pricing for Oracle/SQL Server/Db2. Bring your own media is for RDS Custom, Marketplace is for Db2 mpl |
limitlesspreview |
1 | Yes | Related to Aurora Limitless Database preview? |
location |
38 | AWS GovCloud (US-East) / AWS GovCloud (US-West) / Africa (Cape Town) / Any …etc | Region display name |
locationType |
2 | AWS Outposts / AWS Region | Whether it's a standard region or Outposts |
maxVolumeSize |
4 | 128 TB / 16 TB / 3 TB / 64 TB | Maximum storage size |
memory |
43 | 0.613 GiB / 1 GiB / 1.7 GiB / 1024 GiB …etc | Memory capacity |
minVolumeSize |
3 | 10 GB / 100 GB / 5 GB | Minimum storage size |
networkPerformance |
66 | 10 Gbps / 10 Gigabit / 10,000 Mbps / 100 Gbps …etc | Network performance |
normalizationSizeFactor |
38 | 0.5 / 1.3 / 10.4 / 1152 …etc | Normalization factor used for RI size flexibility calculations. NA for all SQL Server editions and Oracle SE2 (li) |
OfferingClass |
1 | standard | RI offering class. EC2 also has convertible but RDS only has standard |
operation |
53 | CreateDBInstance:0002 / CreateDBInstance:0003 / CreateDBInstance:0004 / CreateDBInstance:0005 …etc | Billing operation code. The last 4 digits match engineCode |
physicalProcessor |
21 | 5th Generation AMD EPYC / AWS Graviton2 / AWS Graviton3 / AWS Graviton4 …etc | Physical processor type |
processorArchitecture |
3 | 32-bit or 64-bit / 64-bit / x86 | Processor architecture |
processorFeatures |
4 | Intel AVX, Intel AVX2, Intel AVX512, Intel Turbo / Intel AVX, Intel AVX2, Intel Turbo / Intel AVX; Intel AVX2; Intel Turbo / Intel AVX; Intel Turbo | Processor instruction set extensions. Delimiters are mixed between , and ; |
productFamily |
13 | Aurora Global Database / CPU Credits / Database Instance / Database Storage / Limitless / Performance Insights / Provisioned IOPS / Provisioned Throughput / RDSProxy / ServerlessV2 / Serverless / Storage Snapshot / System Operation | Billing target type. Specify Database Instance to retrieve only instance hourly charges |
PurchaseOption |
3 | All Upfront / No Upfront / Partial Upfront | RI payment method |
regionCode |
37 | af-south-1 / ap-east-1 / ap-east-2 / ap-northeast-1 …etc | Region code |
Restriction |
1 | Limited SKU Usage | Only appears in Aurora Data API request billing |
servicecode |
1 | AmazonRDS | Service code. Fixed value since already specified with --service-code |
servicename |
1 | Amazon Relational Database Service | Service name |
storage |
47 | 1 x 118 NVMe SSD / 1 x 120 SSD / 1 x 150 NVMe SSD / 1 x 160 SSD …etc | Storage configuration |
storageMedia |
3 | AmazonS3 / Magnetic / SSD | Storage media type |
termType |
2 | OnDemand / Reserved | Pricing type |
unbundledLicensing |
2 | FALSE / TRUE | Whether the license is unbundled |
usagetype |
25806 | AFS1-ASv2-ExtendedSupport:Yr1-Yr2:AuroraPostgreSQL13 / AFS1-Aurora:BackupUsage …etc | Usage type appearing in billing details |
vcpu |
16 | 0 / 128 / 12 / 16 …etc | Number of vCPUs |
volumeName |
4 | gp2 / gp3 / io1 / io2 | EBS volume name. Used in storage pricing records |
volumeType |
9 | General Purpose (SSD) / General Purpose-Aurora / General Purpose-GP3 / General Purpose / IO Optimized-Aurora / Magnetic / Provisioned IOPS (SSD) / Provisioned IOPS-IO2 / Provisioned IOPS | Volume type display name |
windowslicensemultiplier |
2 | 1 / 2 | Windows license multiplier |
Even narrowed down to Amazon RDS, this many attributes and values exist. While some attributes/values are not related to On-Demand pricing, it's clear that it's not enough to just care about region and DB engine.
And when I previously retrieved Amazon RDS Reserved Instance information, I used aws rds describe-reserved-db-instances-offerings, but I noticed that Reserved Instance offering information can also be retrieved with aws pricing as well. (For now, I'll continue to retrieve only On-Demand pricing in this post.)
Drilling Down into the List of engineCode Values
The enginecode attribute values caught my attention, so let me check the full list. The last line displays them side by side.
aws pricing get-attribute-values \
--service-code AmazonRDS \
--attribute-name engineCode \
--region us-east-1 \
--output json \
| jq -r '.AttributeValues[].Value' | sort -n | paste -sd' ' -
0 2 3 4 5 6 8 9 10 11 12 14 15 16 18 19 20 21 27 28 29 34 35 50 51 52 53 210 220 230 231 232 240 241 401 402 403 405 406 407 410 411 420
There are all sorts of codes.
Let me check the DB engine, DB edition, license model, and media type for each code.
aws pricing get-attribute-values \
--service-code AmazonRDS \
--attribute-name engineCode \
--region us-east-1 \
--output json \
| jq -r '.AttributeValues[].Value' | sort -n | while IFS= read -r c; do
aws pricing get-products \
--service-code AmazonRDS \
--region us-east-1 \
--output json \
--filters "Type=TERM_MATCH,Field=engineCode,Value=$c" \
"Type=TERM_MATCH,Field=productFamily,Value=Database Instance" \
| jq -r --arg c "$c" '
[.PriceList[] | fromjson | .product.attributes] as $r
| def u(f): ($r | map(f // "-") | unique | join(" / ")) | if . == "" then "-" else . end;
[$c, ($r | length | tostring),
u(.databaseEngine), u(.databaseEdition), u(.licenseModel),
u(.engineMediaType), u(.deploymentModel)]
| @tsv'
done | { printf 'code\tCount\tengine\tedition\tlicense\tmedia\tdeployModel\n'; cat; } | column -t -s$'\t'
(Since this targets tens of thousands of records without filtering by region, it took over 10 minutes to complete.)
Here's a summary based on the results.
| code | databaseEngine | databaseEdition | licenseModel | engineMediaType | Notes |
|---|---|---|---|---|---|
| 0 | Any | — | — | — | Used for storage charges etc. |
| 2 | MySQL | — | No license required | — | |
| 3 | Oracle | Standard One | — | — | Storage charges only. Discontinued edition |
| 4 | Oracle | Standard | Bring your own license | — | Discontinued edition. Only 1 record remains in ap-south-1 |
| 5 | Oracle | Enterprise | Bring your own license | — / Customer-provided | Only db.r5b series has Customer-provided |
| 6 | Oracle | Standard One | — | — | Storage charges only. Discontinued edition |
| 8 | SQL Server | Standard | — | — | Storage/PIOPS charges only |
| 9 | SQL Server | Enterprise | — | — | Storage/PIOPS charges only |
| 10 | SQL Server | Express | License included | — | |
| 11 | SQL Server | Web | License included | — | |
| 12 | SQL Server | Standard | License included | — | |
| 14 | PostgreSQL | — | No license required | — | |
| 15 | SQL Server | Enterprise | License included | — | |
| 16 | Aurora MySQL | — | No license required | — | |
| 18 | MariaDB | — | No license required | — | |
| 19 | Oracle | Standard Two | Bring your own license | — | |
| 20 | Oracle | Standard Two | License included | — | |
| 21 | Aurora PostgreSQL | — | No license required | — | |
| 27 | Db2 | Community | Bring your own license | — | |
| 28 | Db2 | Standard | Bring your own license | — | |
| 29 | Db2 | Advanced | Bring your own license | — | |
| 34 | Db2 | Standard | Marketplace | — | |
| 35 | Db2 | Advanced | Marketplace | — | |
| 50 | SQL Server | Standard Developer | Bring your own media | — | |
| 51 | SQL Server | Enterprise Developer | Bring your own media | — | |
| 52 | SQL Server | Standard | Bring your own media | Customer-provided | |
| 53 | SQL Server | Enterprise | Bring your own media | Customer-provided | |
| 210 | MySQL (on-premise for Outpost) | — | No license required | — | Outposts |
| 220 | PostgreSQL (on-premise for Outpost) | — | No license required | — | Outposts |
| 230 | SQL Server (on-premise for Outpost) | Enterprise | License included | — | Outposts |
| 231 | SQL Server (on-premise for Outpost) | Standard | License included | — | Outposts |
| 232 | SQL Server (on-premise for Outpost) | Web | License included | — | Outposts |
| 240 | Oracle (on-premises for Outposts) | Enterprise | Bring your own license | — | Outposts |
| 241 | Oracle (on-premises for Outposts) | Standard Two | Bring your own license | — | Outposts |
| 401 | SQL Server | Web | NA | AWS-provided | RDS Custom |
| 402 | SQL Server | Standard | NA | AWS-provided | RDS Custom |
| 403 | SQL Server | Enterprise | NA | AWS-provided | RDS Custom |
| 405 | SQL Server | Standard | NA | Customer-provided | RDS Custom |
| 406 | SQL Server | Enterprise | NA | Customer-provided | RDS Custom |
| 407 | SQL Server | Developer | NA | Customer-provided | RDS Custom |
| 410 | Oracle | Enterprise | Bring your own license | Customer-provided | RDS Custom |
| 411 | Oracle | Standard Two | Bring your own license | Customer-provided | RDS Custom |
I feel like shaking hands with the DB engines that have only one corresponding code (MySQL, PostgreSQL, Aurora MySQL, MariaDB, Aurora PostgreSQL). On the other hand, I'd prefer to keep some distance from SQL Server, Oracle, and Db2, which have many variations.
Getting Amazon RDS On-Demand Pricing
Based on the list of attributes we've reviewed so far, we'll use get-products to retrieve on-demand pricing.
For this exercise, we want to retrieve information under the following conditions:
- Retrieve a list of on-demand pricing for a specific DB engine in a specific region
- The retrieved information should consist of the following columns:
- DB instance class
- Whether Multi-AZ or not
- On-demand price
Here is an example command to retrieve on-demand pricing for MySQL in the Tokyo region:
REGION=ap-northeast-1
DBENGINE=MySQL
TS=$(date +%Y%m%d_%H%M%S)
OD_CSV="rds_ondemand_${DBENGINE}_${REGION}_${TS}.csv"
aws pricing get-products \
--service-code AmazonRDS \
--region us-east-1 \
--output json \
--filters "Type=TERM_MATCH,Field=regionCode,Value=$REGION" \
"Type=TERM_MATCH,Field=databaseEngine,Value=$DBENGINE" \
"Type=TERM_MATCH,Field=productFamily,Value=Database Instance" \
| jq -r '
.PriceList[] | fromjson
| .product.attributes as $a
| select($a.deploymentOption=="Single-AZ" or $a.deploymentOption=="Multi-AZ")
| (.terms.OnDemand // {})[]?.priceDimensions[]?
| select(.unit=="Hrs")
| [$a.instanceType, $a.deploymentOption, .pricePerUnit.USD]
| @csv
' | sort -t, -k1,1 -k2,2 > /tmp/ondemand_body.csv
{ echo '"DBInstanceClass","MultiAZ","OnDemand"'; cat /tmp/ondemand_body.csv; } > "$OD_CSV"
echo "$OD_CSV"
Running this will output the CSV filename as shown below:
rds_ondemand_MySQL_ap-northeast-1_20260929_014420.csv
The contents of the CSV file will list the DB instance class, Multi-AZ status, and on-demand price as shown below:
% head -n 15 rds_ondemand_MySQL_ap-northeast-1_20260929_014306.csv
"DBInstanceClass","MultiAZ","OnDemand"
"db.m1.large","Multi-AZ","0.5800000000"
"db.m1.large","Single-AZ","0.2900000000"
"db.m1.medium","Multi-AZ","0.2900000000"
"db.m1.medium","Single-AZ","0.1450000000"
"db.m1.small","Multi-AZ","0.1500000000"
"db.m1.small","Single-AZ","0.0750000000"
"db.m1.xlarge","Multi-AZ","1.1700000000"
"db.m1.xlarge","Single-AZ","0.5850000000"
"db.m2.2xlarge","Multi-AZ","1.4800000000"
"db.m2.2xlarge","Single-AZ","0.7400000000"
"db.m2.4xlarge","Multi-AZ","2.9500000000"
"db.m2.4xlarge","Single-AZ","1.4750000000"
"db.m2.xlarge","Multi-AZ","0.7300000000"
"db.m2.xlarge","Single-AZ","0.3650000000"
After that, you can retrieve the on-demand pricing list for other engines by simply changing the variables at the top — and that would be that.
……Or so we thought.
Things to Consider Per DB Engine
With regard to what we're trying to accomplish here, there are attributes that need to be considered for each DB engine. The attributes marked with ● in the table below require specification or filtering.
| DB Engine | engine | edition | license | storage | depModel | depOption |
|---|---|---|---|---|---|---|
| MySQL | ● | ● | ||||
| PostgreSQL | ● | ● | ||||
| MariaDB | ● | |||||
| Aurora MySQL | ● | ● | ||||
| Aurora PostgreSQL | ● | ● | ||||
| Oracle | ● | ● | ● | ● | ||
| SQL Server | ● | ● | ● | ● | ||
| Db2 | ● | ● | ● |
The column names in the table represent the following:
| Column Name | Attribute | Description |
|---|---|---|
| engine | databaseEngine |
Required for all engines |
| edition | databaseEdition |
Required for engines that have editions |
| license | licenseModel |
Required for engines that have license models (such as BYOL) |
| storage | storage |
For Aurora, specification of whether Aurora IO Optimization Mode is used or not is required |
| depModel | deploymentModel |
Specification of whether Custom is used or not is required |
| depOption | deploymentOption |
Filtering is required when limiting to only Single-AZ/Multi-AZ |
While adding more columns to the retrieved information would eliminate the need for filtering, since we want to match the format of the RDS Reserved Instance pricing information obtained in the previous blog, we'll handle this carefully.
For example, regarding storage, Aurora has a mode called Aurora IO Optimization Mode, which results in higher on-demand pricing compared to the standard mode.
"DBInstanceClass","MultiAZ","OnDemand","Storage"
"db.r5.12xlarge","Single-AZ","10.9200000000","Aurora IO Optimization Mode"
"db.r5.12xlarge","Single-AZ","8.4000000000","EBS Only"
Without filtering and without including the storage value as a column, it's impossible to tell which mode's on-demand price was retrieved. Therefore, if we don't want to add more columns (keeping it at 3 columns), filtering is necessary.
Regarding deploymentOption, for MySQL and PostgreSQL, in addition to Single-AZ/Multi-AZ, the value Multi-AZ (readable standbys) is also possible. This represents a Multi-AZ DB cluster. Filtering is required if this becomes noise.
Server-Side Filtering and Client-Side Filtering
In the commands used here, the --filters option can be used for server-side filtering, and jq's select can be used for client-side filtering.
It is generally better to filter on the server side as it reduces the amount of data handled locally. The following types can be used with --filters:
| Type | Behavior |
|---|---|
TERM_MATCH |
Returns only items where the specified attribute matches the specified value |
EQUALS |
Returns items with values that exactly match the specified value |
CONTAINS |
Returns items with values that contain the specified value as a substring |
ANY_OF |
Returns items with values that match any of the specified multiple values |
NONE_OF |
Returns items with values that do not match any of the specified multiple values |
An example of filtering that is as generic as possible regardless of the DB engine might look like this. (Of course, the conditions will differ depending on what values you want to retrieve.)
REGION=ap-northeast-1
DBENGINE=MySQL
aws pricing get-products \
--service-code AmazonRDS \
--region us-east-1 \
--output json \
--filters "$(jq -cn --arg region "$REGION" --arg engine "$DBENGINE" '[
{Type:"TERM_MATCH", Field:"regionCode", Value:$region},
{Type:"TERM_MATCH", Field:"databaseEngine", Value:$engine},
{Type:"TERM_MATCH", Field:"productFamily", Value:"Database Instance"},
{Type:"ANY_OF", Field:"deploymentOption", Value:"Single-AZ,Multi-AZ"},
{Type:"NONE_OF", Field:"storage", Value:"Aurora IO Optimization Mode"},
{Type:"NONE_OF", Field:"deploymentModel", Value:"Custom"}
]')" \
| jq -r '
.PriceList[] | fromjson
……
If writing --filters in JSON syntax seems a bit verbose, you can use the shorthand syntax and leave the filtering to the jq side. Choose whichever is clearer. (Note that the shorthand syntax does not support values containing commas, such as Single-AZ,Multi-AZ.)
……
--filters "Type=TERM_MATCH,Field=regionCode,Value=$REGION" \
"Type=TERM_MATCH,Field=databaseEngine,Value=$DBENGINE" \
"Type=TERM_MATCH,Field=productFamily,Value=Database Instance" \
| jq -r '
.PriceList[] | fromjson
| .product.attributes as $a
| select($a.deploymentOption=="Single-AZ" or $a.deploymentOption=="Multi-AZ")
| select($a.storage != "Aurora IO Optimization Mode")
| select(($a.deploymentModel // "") != "Custom")
……
When Dealing with Editions and Licenses, Using engineCode Is Easier
I mentioned "an example of filtering that is as generic as possible regardless of the DB engine" and listed several examples above, but when considering Oracle, SQL Server, and Db2 as targets, filtering by databaseEdition and licenseModel is also necessary.
To avoid writing complex conditional branches while still wanting a command that works for any DB engine just by changing environment variables, we'll use engineCode as the filtering condition.
That said, there are many types of engineCode as we explored earlier, so this time we'll limit it to the types that can be confirmed in the Amazon RDS Reserved Instance offerings.
% aws rds describe-reserved-db-instances-offerings \
--region ap-northeast-1 \
--output json \
| jq -r '[.ReservedDBInstancesOfferings[].ProductDescription] | unique[]'
aurora-mysql
aurora-postgresql
custom-sqlserver-ee(byol)
custom-sqlserver-ee(li)
custom-sqlserver-se(byol)
custom-sqlserver-se(li)
custom-sqlserver-web(li)
db2-ae(byol)
db2-ae(mpl)
db2-se(byol)
db2-se(mpl)
mariadb
mysql
oracle-ee(byol)
oracle-se2 (byol)
oracle-se2(li)
postgresql
sqlserver-ee(li)
sqlserver-ex(li)
sqlserver-se(li)
sqlserver-web(li)
We'll pre-map the engine names above (technically ProductDescription) to their corresponding engineCode values, and use a method of specifying the engine name to filter.
REGION=ap-northeast-1
ENGINE="oracle-ee(byol)"
TS=$(date +%Y%m%d_%H%M%S)
engine_code() {
case "$(echo "$1" | tr -d ' ')" in
"mysql") echo 2 ;;
"postgresql") echo 14 ;;
"mariadb") echo 18 ;;
"aurora-mysql") echo 16 ;;
"aurora-postgresql") echo 21 ;;
"oracle-ee(byol)") echo 5 ;;
"oracle-se2(byol)") echo 19 ;;
"oracle-se2(li)") echo 20 ;;
"sqlserver-ee(li)") echo 15 ;;
"sqlserver-se(li)") echo 12 ;;
"sqlserver-web(li)") echo 11 ;;
"sqlserver-ex(li)") echo 10 ;;
"db2-ae(byol)") echo 29 ;;
"db2-ae(mpl)") echo 35 ;;
"db2-se(byol)") echo 28 ;;
"db2-se(mpl)") echo 34 ;;
"custom-sqlserver-ee(byol)") echo 406 ;;
"custom-sqlserver-ee(li)") echo 403 ;;
"custom-sqlserver-se(byol)") echo 405 ;;
"custom-sqlserver-se(li)") echo 402 ;;
"custom-sqlserver-web(li)") echo 401 ;;
*) echo "" ;;
esac
}
ENGINE_CODE=$(engine_code "$ENGINE")
ENGINE_SLUG=$(echo "$ENGINE" | sed -E 's/[[:space:]]+//g; s/[()]/_/g; s/_+$//')
OD_CSV="rds_ondemand_${ENGINE_SLUG}_${REGION}_${TS}.csv"
aws pricing get-products \
--service-code AmazonRDS \
--region us-east-1 \
--output json \
--filters "Type=TERM_MATCH,Field=regionCode,Value=$REGION" \
"Type=TERM_MATCH,Field=engineCode,Value=$ENGINE_CODE" \
"Type=TERM_MATCH,Field=productFamily,Value=Database Instance" \
| jq -r '
.PriceList[] | fromjson
| .product.attributes as $a
| select($a.deploymentOption=="Single-AZ" or $a.deploymentOption=="Multi-AZ")
| select($a.storage != "Aurora IO Optimization Mode")
| (.terms.OnDemand // {})[]?.priceDimensions[]?
| select(.unit=="Hrs")
| [$a.instanceType, $a.deploymentOption, .pricePerUnit.USD]
| @csv
' | sort -t, -k1,1 -k2,2 > /tmp/ondemand_body.csv
{ echo '"DBInstanceClass","MultiAZ","OnDemand"'; cat /tmp/ondemand_body.csv; } > "$OD_CSV"
echo "$OD_CSV"
Here is what the execution looks like. The CSV filename is displayed.
rds_ondemand_oracle-ee_byol_ap-northeast-1_20260929_145201.csv
The contents look like this. We were able to retrieve on-demand pricing while maintaining 3 columns and outputting separate files per edition and license.
% head -n 15 rds_ondemand_oracle-ee_byol_ap-northeast-1_20260929_145201.csv
"DBInstanceClass","MultiAZ","OnDemand"
"db.m3.2xlarge","Multi-AZ","1.9300000000"
"db.m3.2xlarge","Single-AZ","0.9650000000"
"db.m3.large","Multi-AZ","0.4800000000"
"db.m3.large","Single-AZ","0.2400000000"
"db.m3.medium","Multi-AZ","0.2400000000"
"db.m3.medium","Single-AZ","0.1200000000"
"db.m3.xlarge","Multi-AZ","0.9700000000"
"db.m3.xlarge","Single-AZ","0.4850000000"
"db.m4.10xlarge","Multi-AZ","10.1740000000"
"db.m4.10xlarge","Single-AZ","5.0870000000"
"db.m4.16xlarge","Multi-AZ","16.2816000000"
"db.m4.16xlarge","Single-AZ","8.1408000000"
"db.m4.2xlarge","Multi-AZ","2.0340000000"
"db.m4.2xlarge","Single-AZ","1.0170000000"
Summary (Reprinted)
- You can retrieve Amazon RDS on-demand pricing using
aws pricing get-attribute-values- Reserved Instance pricing can also be retrieved (not covered in this blog)
- There are several attributes to consider when running
aws pricing get-attribute-values. These attributes differ depending on the DB engine.
Closing
This was a write-up about retrieving Amazon RDS on-demand pricing using the AWS Price List Query API.
I found that the attributes to consider differ by DB engine, and that some ingenuity is required when trying to retrieve information generically. I started with the light intention of "just wanting to get all on-demand prices at once," but ended up feeling like I had glimpsed into the abyss.
I hope this is useful for those who venture into the abyss.
That's all from チバユキ (@batchicchi).
