Build a Serverless Barcode Reading API with Python and AWS Lambda

In the previous article, we built a simple barcode reader server in Python and deployed it on Vercel. In this article, we are going to deploy it on AWS Lambda to provide a serverless barcode reading interface.

What you’ll build: A serverless REST API on AWS Lambda that accepts a base64-encoded image and returns decoded barcode data (format, text, coordinates) using the Dynamsoft Barcode Reader Python SDK.

Key Takeaways

  • AWS Lambda can run the Dynamsoft Barcode Reader Python SDK as a serverless function with no persistent infrastructure.
  • Lambda Layers package the SDK wheel so the handler stays lightweight and deploys cleanly.
  • The /tmp directory is the only writable path in Lambda — license cache must be directed there.
  • An API Gateway trigger exposes the Lambda as a standard HTTPS endpoint that any client can call.

Common Developer Questions

  • How do I read barcodes in AWS Lambda using Python?
  • How do I package a Python barcode SDK as an AWS Lambda Layer?
  • Why does the Dynamsoft license cache fail in AWS Lambda and how do I fix it?

Prerequisites

Step 1: Create a New Lambda Function

Create a new AWS Lambda function. Here, we do this in the AWS console.

New function

Step 2: Add the Barcode SDK as a Lambda Layer

A Lambda layer is a .zip file archive that contains supplementary code or data. Layers usually contain library dependencies, a custom runtime, or configuration files.

Here, we need to add the package of Dynamsoft Barcode Reader in the layer.

  1. Download the wheel file of Dynamsoft Barcode Reader from pypi.
  2. Unzip it into a folder named python.
  3. Zip the folder for uploading as a layer.

New layer

Remember to use the layer for the function:

Use layer

Step 3: Write the Barcode Reading Handler

Next, modify the handler to use Dynamsoft Barcode Reader.

The handler receives a request like the following:

{
  "base64":"<base64-encoded-image>"
}

Then outputs the following:

{"results": 
  [
    {
      "barcodeFormat": "QR_CODE", 
      "barcodeText": "https://www.dynamsoft.com",
      "barcodeBytes": "aHR0cHM6Ly93d3cuZHluYW1zb2Z0LmNvbQ==",
      "confidence": 82,
      "x1": 7,
      "y1": 7,
      "x2": 93,
      "y2": 6,
      "x3": 94,
      "y3": 94,
      "x4": 6,
      "y4": 93
    }
  ]
}
  1. Import Dynamsoft Barcode Reader and initialize its license. It needs to write caches so that if the license is valid, it does not need to connect to the license server again. Since in AWS Lambda, we can only write to “/tmp”, we need to set a path for saving the cache.

    import os
    from dynamsoft_barcode_reader_bundle import *
    folder_path = "/tmp/dynamsoft"
    if not os.path.exists(folder_path):
        os.makedirs(folder_path)
    # Sets a directory path for saving the license cache.
    LicenseManager.set_license_cache_path(folder_path)
    errorCode, errorMsg = LicenseManager.init_license(DynamsoftLicense)
    

    You can apply for a trial license here.

  2. In the handler, create a capture vision router instance to read barcodes from the base64.

    import base64
    def lambda_handler(event, context):
        if errorCode != EnumErrorCode.EC_OK and errorCode != EnumErrorCode.EC_LICENSE_CACHE_USED:
            return ("License error: "+ errorMsg)
    
        cvr_instance = CaptureVisionRouter()
        request_body = event["body"]
        base64_string = json.loads(request_body)["base64"]
        image_bytes = base64.b64decode(base64_string)
        result = cvr_instance.capture(image_bytes, EnumPresetTemplate.PT_READ_BARCODES.value)
        barcode_result = result.get_decoded_barcodes_result()
        result_dict = {}
        results = []
    
        if barcode_result != None:
            for br in barcode_result.get_items():
                result = {}
                result["barcodeFormat"] = br.get_format_string()
                result["barcodeText"] = br.get_text()
                result["barcodeBytes"] = str(base64.b64encode(br.get_bytes()))[2:-1]
                result["confidence"] = br.get_confidence()
                quad = br.get_location()
                points = quad.points
                result["x1"] = points[0].x
                result["y1"] = points[0].y
                result["x2"] = points[1].x
                result["y2"] = points[1].y
                result["x3"] = points[2].x
                result["y3"] = points[2].y
                result["x4"] = points[3].x
                result["y4"] = points[3].y
                results.append(result)
        result_dict["results"] = results
        return {
            "statusCode": 200,
            "body": json.dumps(result_dict)
        }
    

Step 4: Expose the Function via API Gateway

We can add an API gateway as the trigger of the function.

New trigger

Then, we can call the Lambda function through HTTP.

Here is the Python script to test it:

import base64
import json
import requests

endpoint = "" #like https://*****.execute-api.us-east-2.amazonaws.com/default/BarcodeReader

def get_picture_base64_data(image_path):
    with open(image_path, 'rb') as image_file:
        base64_data = base64.b64encode(image_file.read())
    return base64_data.decode('utf-8')
    
def decode():
    base64 = get_picture_base64_data("./sample_qr.png")
    body = {"base64": base64}
    json_data = json.dumps(body)
    headers = {'Content-type': 'application/json'}
    r = requests.post(endpoint, data=json_data, headers=headers)
    print(r.json())

if __name__ == "__main__":
    decode()

Common Issues and Edge Cases

  • License cache write failure: AWS Lambda only allows writing to /tmp. If you don’t call LicenseManager.set_license_cache_path("/tmp/dynamsoft"), the SDK will fail silently or throw a permission error on every cold start.
  • Layer size limit exceeded: AWS Lambda layers have a 250 MB unzipped limit. If the Dynamsoft wheel plus dependencies exceed this, extract only the required platform-specific binaries (Linux x86_64) before zipping.
  • Cold start latency: The first invocation after deployment or idle timeout loads the SDK into memory, which can add 3–8 seconds. Subsequent warm invocations are significantly faster. Use provisioned concurrency if latency matters.

Source Code

The source code of the project is available here: https://github.com/tony-xlh/barcode-reader-aws-lambda/