Lambda’s asynchronous invocation is surprisingly more about eventual consistency than it is about offloading work.

Let’s see it in action. Imagine a simple Lambda function that just logs its input:

import json

def lambda_handler(event, context):
    print("Received event: " + json.dumps(event))
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

Now, let’s invoke this function asynchronously using the AWS SDK (Boto3 for Python).

import boto3

lambda_client = boto3.client('lambda')

response = lambda_client.invoke(
    FunctionName='your-async-function-name',
    InvocationType='Event',  # This is the key for async
    Payload='{"message": "This is an async message"}'
)

print(f"Async invocation response: {response}")

When you run this code, the response you get back from the invoke API call looks something like this:

Async invocation response: {'StatusCode': 202, 'ResponseMetadata': {'RequestId': 'a1b2c3d4-e5f6-7890-1234-abcdef123456', 'HTTPStatusCode': 202, 'HTTPHeaders': {'date': 'Mon, 01 Jan 2024 12:00:00 GMT', 'content-type': 'application/json', 'content-length': '2', 'connection': 'keep-alive', 'x-amzn-requestid': 'a1b2c3d4-e5f6-7890-1234-abcdef123456'}, 'RetryAttempts': 0}}

Notice the StatusCode: 202. This isn’t the status code of your Lambda function’s execution; it’s the status code of the invocation request itself. It means "Accepted for processing." You don’t get the result of your function back immediately. Lambda queues the event and executes your function in the background.

Contrast this with a synchronous invocation:

import boto3

lambda_client = boto3.client('lambda')

response = lambda_client.invoke(
    FunctionName='your-sync-function-name',
    InvocationType='RequestResponse', # This is the key for sync
    Payload='{"message": "This is a sync message"}'
)

print(f"Sync invocation response: {response}")

The response for a synchronous call will include StatusCode: 200 (if your function ran successfully) and, critically, a FunctionEventInvokeSettings object containing the Payload directly from your function’s return value.

The core problem async invocation solves is decoupling. The caller doesn’t need to wait for the function to finish. This is perfect for scenarios where the caller’s immediate task is complete once the work is initiated, not finished. Think of sending an email, writing a log, or triggering another downstream process that can tolerate a slight delay.

Here’s how it works under the hood: when you choose InvocationType='Event', Lambda doesn’t execute your function directly in the context of the invoke API call. Instead, it publishes the Payload to an internal queue. A separate Lambda execution environment (or an existing one if available) picks up this event from the queue and runs your function. If an execution environment isn’t available, Lambda spins one up. This separation is why you don’t get a direct result back; the invoke call is just the "fire and forget" part.

The key levers you control are the FunctionName and InvocationType. For async, it’s InvocationType='Event'. For sync, it’s InvocationType='RequestResponse'. There’s also DryRun, which just checks permissions without actually invoking.

The default behavior for most AWS services integrating with Lambda (like S3 events, SQS messages, SNS topics) is asynchronous invocation. This is a fundamental design choice that allows those services to remain responsive. If S3 had to wait for every Lambda function triggered by an object upload to complete, uploads would take ages.

When you invoke a Lambda function asynchronously, the Invoke API call returns immediately with a 202 Accepted status code. This means AWS has accepted your request to invoke the function and will queue the event. The actual execution of your Lambda function happens later, managed by Lambda’s internal event processing system. You do not receive the return value of your function in the response; instead, you’ll find logs and metrics related to the execution in CloudWatch. Crucially, if your function fails during an asynchronous invocation, Lambda will automatically retry the invocation a few times by default, which is a key aspect of its "at-least-once" delivery guarantee for many event sources.

The next hurdle you’ll likely encounter is understanding how to handle failures and duplicates in asynchronous processing.

Want structured learning?

Take the full Lambda course →