We stand with Ukraine
Go Wombat logo

How to Properly Use Serverless Technologies and AWS Lambda

Article by

Updated on February 20, 2026

Read — 10 minutes

I recently read, and shared with the team, an article on using serverless technologies for a web API.

In short, the author researched whether to move his API from its existing setup to serverless. He found that Lambda was a little slower than an EC2 instance, but the main problem was the price, and specifically the price of API Gateway rather than Lambda itself.

I liked his research and his approach. It also reminded us of a case where Lambda worked well for us.

Lambda functions are a good fit for small, independent functions, as the name suggests, and especially for calling many different APIs or fetching data from many web pages. You can invoke as many Lambda functions in parallel as you need, and they scale without changes to the infrastructure code, up to your account’s concurrency limit. By default, AWS sets that limit at 1,000 concurrent executions across all functions in a Region.

Lambda does not start a separate server with its own IP address for each call: execution environments are reused between invocations (warm starts). AWS also publishes its IP address ranges, so websites can easily identify and block traffic that comes from AWS.

Example

In this example, we fetch the latest exchange rate between two cryptocurrencies from several exchanges in parallel.

We’ll use CCXT for that. It supports more than 100 cryptocurrency exchanges and has a Python implementation.

First, create a simple Lambda function that retrieves data for the specified tickers from the specified exchange.

import json
import ccxt


def lambda_handler(event, context):
    id = event.get('exchanges')

    exchange_found = id in ccxt.exchanges

    output = []
    if exchange_found:
        exchange = getattr(ccxt, id)()

        for symbol in event.get('symbols'):
            ticker = exchange.fetch_ticker(symbol.upper())
            output.append({'exchange': id, 'symbol': symbol, 'ticker': ticker})

        return {'statusCode': 200, 'body': json.dumps(output)}

    return {
        'statusCode': 400,
        'body': json.dumps(f'Exchange {id} not found')
    }

Once the Lambda function is ready, the next step is to run it. We retrieve data from many sources, and it has to be fast because rates change quickly, so we make the code asynchronous and invoke the Lambda functions concurrently.

To invoke AWS Lambda from Python, you use Boto3, the AWS SDK for Python. Here we use aioboto3, a third-party async wrapper around boto3 and aiobotocore.

client = aioboto3.client(
    'lambda',
    region_name='[YOUR_LAMBDA_REGION]',
    aws_access_key_id='[YOUR_AWS_KEY_ID]',
    aws_secret_access_key='[YOUR_AWS_KEY]'
)
response = await client.invoke(
    FunctionName='[YOUR_LAMBDA_FUNCTION_NAME_OR_ARN]',
    InvocationType='RequestResponse',
    LogType='Tail',
    Payload=json.dumps([PAYLOAD]),
)

Here is a simple example of invoking a Lambda function; see the Boto3 docs for more details.

And here is the runner I ended up with:

import ast
import asyncio
import json

import aioboto3


class LambdaInvoker:
    def __init__(self):
        self.client = aioboto3.client(
            'lambda',
            region_name='[YOUR_LAMBDA_REGION]',
            aws_access_key_id='[YOUR_AWS_KEY_ID]',
            aws_secret_access_key='[YOUR_AWS_KEY]'
        )

    async def invoke(self, payloads):
        results = await asyncio.gather(*map(self.__invoke_lambda, payloads))
        return results

    async def __invoke_lambda(self, payload):
        print(f'Invoking {payload["exchanges"]}')
        response = await self.client.invoke(
            FunctionName='[YOUR_LAMBDA_FUNCTION_NAME_OR_ARN]',
            InvocationType='RequestResponse',
            LogType='Tail',
            Payload=json.dumps(payload),
        )
        result = await response['Payload'].read()
        print(f'Finished {payload["exchanges"]}')

        return result

    async def close(self):
        await self.client.close()

    @staticmethod
    def print_result(result):
        data_dict = ast.literal_eval(result.decode('utf-8'))
        body_json = data_dict.get("body")
        if body_json:
            body = json.loads(body_json)
            print(json.dumps(body, indent=4))
        else:
            print(json.dumps(data_dict, indent=4))


if __name__ == '__main__':
    payloads = [
        {'exchanges': 'binance', 'symbols': ['BTC/USDT', 'ETH/BTC']},
        {'exchanges': 'cex', 'symbols': ['BTC/USD', 'ETH/BTC']},
        {'exchanges': 'bitfinex', 'symbols': ['BTC/USDT', 'ETH/BTC']},
        {'exchanges': 'kraken', 'symbols': ['BTC/USD', 'ETH/BTC']},
        {'exchanges': 'poloniex', 'symbols': ['BTC/USDT', 'ETH/BTC']}
    ]

    lambda_invoker = LambdaInvoker()

    loop = asyncio.get_event_loop()
    results = loop.run_until_complete(lambda_invoker.invoke(payloads))
    loop.run_until_complete(lambda_invoker.close())

    for result in results:
        LambdaInvoker.print_result(result)

The main logic is in the __invoke_lambda and invoke methods.

__invoke_lambda uses the same code as above to run a Lambda function and get its response. It is an asynchronous method.

invoke maps the __invoke_lambda coroutine to each payload, and asyncio.gather runs them concurrently in one thread, which works well for network calls like these.

Bonus

If you don’t want to write asynchronous code, here is the same using Python’s multiprocessing module:

import json
import multiprocessing as mp

import boto3


def run(payloads):
    pool = mp.Pool(len(payloads))
    return pool.map(invoke_lambda, payloads)


def invoke_lambda(payload):
    print(f'Invoking {payload["exchanges"]}')
    response = client.invoke(
        FunctionName='arn:aws:lambda:eu-central-1:557458395923:function:ccxt-exchange',
        InvocationType='RequestResponse',
        LogType='Tail',
        Payload=json.dumps(payload),
    )
    result = response['Payload'].read()
    print(f'Finished {payload["exchanges"]}')

    return result

How can we help you ?

How can we help youHow can we help youHow can we help you