Search code examples
python-3.xamazon-web-servicesamazon-ec2boto3

Use boto3 to get current price for given EC2 instance type


Now that AWS have a Pricing API, how could one use Boto3 to fetch the current hourly price for a given on-demand EC2 instance type (e.g. t2.micro), region (e.g. eu-west-1) and operating system (e.g. Linux)? I only want the price returned. Based on my understanding, having those four pieces of information should be enough to filter down to a singular result.

However, all the examples I've seen fetch huge lists of data from the API that would have to be post-processed in order to get what I want. I would like to filter the data on the API side, before it's being returned.


Solution

  • Here is the solution I ended up with. Using Boto3's own Pricing API with a filter for the instance type, region and operating system. The API still returns a lot of information, so I needed to do a bit of post-processing.

    import boto3
    import json
    from pkg_resources import resource_filename
    
    # Search product filter. This will reduce the amount of data returned by the
    # get_products function of the Pricing API
    FLT = '[{{"Field": "tenancy", "Value": "shared", "Type": "TERM_MATCH"}},'\
          '{{"Field": "operatingSystem", "Value": "{o}", "Type": "TERM_MATCH"}},'\
          '{{"Field": "preInstalledSw", "Value": "NA", "Type": "TERM_MATCH"}},'\
          '{{"Field": "instanceType", "Value": "{t}", "Type": "TERM_MATCH"}},'\
          '{{"Field": "location", "Value": "{r}", "Type": "TERM_MATCH"}},'\
          '{{"Field": "capacitystatus", "Value": "Used", "Type": "TERM_MATCH"}}]'
    
    
    # Get current AWS price for an on-demand instance
    def get_price(region, instance, os):
        f = FLT.format(r=region, t=instance, o=os)
        data = client.get_products(ServiceCode='AmazonEC2', Filters=json.loads(f))
        od = json.loads(data['PriceList'][0])['terms']['OnDemand']
        id1 = list(od)[0]
        id2 = list(od[id1]['priceDimensions'])[0]
        return od[id1]['priceDimensions'][id2]['pricePerUnit']['USD']
    
    # Translate region code to region name. Even though the API data contains
    # regionCode field, it will not return accurate data. However using the location
    # field will, but then we need to translate the region code into a region name.
    # You could skip this by using the region names in your code directly, but most
    # other APIs are using the region code.
    def get_region_name(region_code):
        default_region = 'US East (N. Virginia)'
        endpoint_file = resource_filename('botocore', 'data/endpoints.json')
        try:
            with open(endpoint_file, 'r') as f:
                data = json.load(f)
            # Botocore is using Europe while Pricing API using EU...sigh...
            return data['partitions'][0]['regions'][region_code]['description'].replace('Europe', 'EU')
        except IOError:
            return default_region
    
    
    # Use AWS Pricing API through Boto3
    # API only has us-east-1 and ap-south-1 as valid endpoints.
    # It doesn't have any impact on your selected region for your instance.
    client = boto3.client('pricing', region_name='us-east-1')
    
    # Get current price for a given instance, region and os
    price = get_price(get_region_name('eu-west-1'), 't3.micro', 'Linux')
    print(price)
    

    This example outputs 0.0114000000 (hourly price in USD) fairly quickly. (This number was verified to match the current value listed here at the date of this writing)