# Print From AWS Lambda to a Local Printer: Python Guide

> Print from AWS Lambda to a local printer using Python and S3. Follow a practical setup with secure access, retries, and duplicate-print protection.

Published 2026-10-27 · https://printscaler.com/blog/print-from-aws-lambda-local-printer

![Cloud workflow connecting document storage and serverless processing to a local computer and warehouse printer](https://printscaler.com/blog/images/print-from-aws-lambda-local-printer-2.jpg)

You can **print from AWS Lambda to a local printer** by sending an HTTPS request to a cloud printing API, with a local agent delivering the job to the printer. Lambda does not need a printer driver, a VPN into your office, or inbound access to your warehouse network. This tutorial builds an S3-to-printer workflow using Python, PrintScaler, and idempotency keys to keep event retries from producing duplicate print jobs.

## Why Lambda cannot simply use your office printer

A Lambda function runs in AWS, not on the computer that has your printer installed. A USB printer is not available to that function, and a printer at a private address such as `192.168.1.50` is not automatically reachable from AWS.

Putting Lambda in a VPC does not, by itself, connect that VPC to your office network. A direct network-printing design requires additional routing, connectivity, and printer-protocol support.

A cloud printing service separates these responsibilities:

- **Lambda** decides what to print and when.
- **The printing API** accepts the document and tracks the job.
- **A local agent** connects the cloud service to an installed printer.
- **The printer and its local configuration** handle physical output.

With PrintScaler, the agent maintains an outbound WebSocket connection. You do not need to expose the printer or open inbound firewall ports for this agent-based workflow.

This is a backend automation pattern. If your starting point is a user clicking Print in a web application, the related guide to [silent printing from a browser](https://printscaler.com/blog/silent-printing-from-browser) covers that different entry point.

## The workflow: upload a PDF to S3, then print it

The example uses an S3 object-created event to invoke a Python Lambda function:

1. Your application uploads a completed PDF to an S3 prefix such as `ready-to-print/`.
2. S3 sends an object-created notification to Lambda.
3. Lambda reads the PDF using its IAM execution role.
4. Lambda sends the PDF to PrintScaler using `pdf_base64`.
5. The PrintScaler agent delivers the job to the selected local printer.

This works for packing slips, invoices, work orders, and other PDFs generated by an application.

The example deliberately uploads the PDF content rather than making the S3 object public. It also avoids making document availability depend on a presigned URL's expiration time.

### Choose the document format before writing code

| Document or device | Suitable API content type | Main consideration |
|---|---|---|
| Invoice or packing-slip PDF | `pdf_base64` or `pdf_uri` | Match paper size and scaling to the document |
| Shipping label supplied as PDF | `pdf_base64` or `pdf_uri` | Preserve the intended physical dimensions |
| ZPL or EPL label instructions | `raw_base64` or `raw_uri` | The printer must understand the supplied language |
| ESC/POS receipt instructions | `raw_base64` or `raw_uri` | Commands must match the receipt printer |

A RAW request does not convert a PDF into printer commands. For this tutorial, start with a valid PDF that already prints correctly on the destination computer.

## Step 1: connect and test the local printer

Install the [PrintScaler agent](https://printscaler.com/download) on a Windows, macOS, Linux, or Raspberry Pi computer with access to the printer. Pair that computer by entering its pairing code in the PrintScaler dashboard.

Before configuring AWS:

1. Confirm the operating system can print to the device.
2. Check that the printer appears in the PrintScaler dashboard.
3. Use the dashboard's test-page button.
4. Record the printer ID you will use in API requests.

The computer must be running for the agent to deliver work immediately. The agent starts automatically after reboots, but it cannot operate while its host is powered off.

For a USB printer, this host is the bridge between AWS and the device. For a network printer installed on the host, it serves the same role without requiring AWS to reach the printer's private IP address.

## Step 2: configure S3, Lambda, and IAM

Create a Lambda function using a supported Python runtime. The code below uses Python's standard library plus Boto3; package and pin your dependencies when you need reproducible deployments.

### Configure the S3 notification

On your document bucket, create an object-created notification with:

- **Prefix:** `ready-to-print/`
- **Suffix:** `.pdf`
- **Destination:** your Lambda function

Your document producer should write only completed, approved documents into this prefix. Uploading a draft there is otherwise a real printing instruction.

Keep generated output, logs, and error artifacts outside the triggering prefix. If the function later writes objects back to S3, this separation also helps prevent recursive invocations.

S3 needs permission to invoke the function. Configure the Lambda resource-based permission for the intended bucket and account; the AWS console can assist when you add the trigger.

### Give Lambda narrowly scoped permissions

The execution role needs:

- `s3:GetObject` for the input prefix.
- `s3:GetObjectVersion` if it will retrieve specific object versions from a versioned bucket.
- CloudWatch Logs permissions for operational logging.
- Appropriate `kms:Decrypt` permission if the input objects use a customer-managed KMS key.

If you retrieve your PrintScaler API key from AWS Secrets Manager, add permission for that specific secret as well.

Avoid public-read access on the bucket. Lambda reads the document through IAM; PrintScaler receives the document in the authenticated API request.

### Add the function configuration

The example reads two environment variables:

| Variable | Example | Purpose |
|---|---|---|
| `PRINTSCALER_API_KEY` | `psk_...` | Authenticates the API request |
| `PRINTSCALER_PRINTER_ID` | `123` | Selects the destination printer |

Environment variables keep credentials out of the source code, but they are still secrets. Restrict configuration access, do not log their values, and consider retrieving the key from Secrets Manager in production.

The function needs outbound HTTPS access to `api.printscaler.com`. If you attach Lambda to a VPC, ensure its networking permits that access; putting it in a public subnet alone does not give it a public IP address.

## Step 3: send the PDF with a Python Lambda function

PrintScaler accepts print jobs at `POST https://api.printscaler.com/v1/printjobs`. This implementation uses Bearer authentication, the documented `pdf_base64` content type, and an `X-Idempotency-Key` header.

The **5 MiB document cap below is an application guardrail**, not a stated PrintScaler limit. Base64 increases payload size by roughly one-third, and the function holds several representations of the document in memory.

```python
import base64
import hashlib
import json
import os
from urllib.parse import unquote_plus
from urllib.request import Request, urlopen

import boto3

s3 = boto3.client("s3")
API_URL = "https://api.printscaler.com/v1/printjobs"
MAX_PDF_BYTES = 5 * 1024 * 1024  # Application policy, not API limit.


def lambda_handler(event, context):
    api_key = os.environ["PRINTSCALER_API_KEY"]
    printer_id = int(os.environ["PRINTSCALER_PRINTER_ID"])
    submitted = 0

    for record in event["Records"]:
        bucket = record["s3"]["bucket"]["name"]
        obj = record["s3"]["object"]
        key = unquote_plus(obj["key"])
        version_id = obj.get("versionId")
        sequencer = obj["sequencer"]

        if not key.lower().endswith(".pdf"):
            continue

        get_params = {"Bucket": bucket, "Key": key}
        if version_id is not None:
            get_params["VersionId"] = version_id

        source = s3.get_object(**get_params)
        stream = source["Body"]
        try:
            pdf = stream.read(MAX_PDF_BYTES + 1)
        finally:
            stream.close()

        if not pdf or len(pdf) > MAX_PDF_BYTES:
            raise ValueError("PDF is empty or exceeds application size cap")

        identity = json.dumps(
            [bucket, key, version_id, sequencer, printer_id],
            separators=(",", ":"),
        )
        idempotency_key = hashlib.sha256(
            identity.encode("utf-8")
        ).hexdigest()

        payload = {
            "printerId": printer_id,
            "contentType": "pdf_base64",
            "content": base64.b64encode(pdf).decode("ascii"),
            "title": key.rsplit("/", 1)[-1],
        }
        request = Request(
            API_URL,
            data=json.dumps(payload).encode("utf-8"),
            headers={
                "Authorization": f"Bearer {api_key}",
                "Content-Type": "application/json",
                "X-Idempotency-Key": idempotency_key,
            },
            method="POST",
        )

        with urlopen(request, timeout=15) as response:
            response.read()
            submitted += 1

    return {"submitted": submitted}
```

The function intentionally does not assume a particular response field contains a job ID. When adding persistent job tracking, use the response schema in the [print jobs API reference](https://printscaler.com/docs#printjobs).

Set the Lambda timeout comfortably above the HTTPS request timeout, allowing for S3 downloads and every record in the event. Test memory usage with realistic files rather than sizing only for a tiny sample PDF.

## Step 4: make event retries safe

S3 notifications can be delivered more than once. Lambda asynchronous processing can also retry failed invocations. A timeout is especially ambiguous: PrintScaler might have accepted the request even though Lambda did not receive the response.

**Do not generate a fresh random idempotency key on every invocation.** That turns a retry into a new printing instruction.

The code derives its key from:

- The bucket and object key.
- The object version, when present.
- The S3 event sequencer.
- The destination printer ID.

A redelivery of the same object event to the same printer therefore uses the same key. PrintScaler's [idempotency support](https://printscaler.com/docs#idempotency) prevents a retried request from creating duplicate prints.

### Keep the source document stable

Use unique S3 keys for immutable documents, or enable versioning and retrieve the version identified in the event. Without either protection, an object could be overwritten before Lambda reads it, causing an earlier event to print newer content.

For example, `ready-to-print/order-84721/packing-slip-v1.pdf` is safer than repeatedly replacing `ready-to-print/latest.pdf`.

If an operator intentionally requests another copy, treat that as a new business action with its own identifier. Do not reuse the original request's idempotency key and expect it to create another job.

### Plan for partial progress

An event can contain multiple records. If the function submits one document and fails on the next, retrying the invocation can revisit the first record. Stable per-document keys protect that already-submitted work.

The sample lets errors propagate so Lambda can recognize failure. For production, configure an asynchronous failure destination or dead-letter queue, and distinguish transient failures from permanent ones. A bad API key or oversized document needs intervention, not endless retries.

## Step 5: test printing and monitor the full lifecycle

Upload one small PDF to the configured prefix. Check these stages independently:

1. **S3 event delivery:** Did Lambda run?
2. **Function execution:** Did it read the intended object and complete the API request?
3. **Print-job processing:** Does the job appear in the PrintScaler dashboard?
4. **Local output:** Did the intended printer produce a usable page?

A successful HTTPS request is not proof that paper has emerged from the device. PrintScaler exposes job states including `new`, `sent_to_client`, `queued`, `in_progress`, `done`, `error`, `cancelled`, and `expired`, with job history for troubleshooting.

For automated monitoring, configure [signed print-job webhooks](https://printscaler.com/docs#webhooks). Verify the HMAC-SHA256 signature before trusting a notification, and make the receiver tolerant of retries.

Keep sensitive PDF contents, API keys, and document URLs out of logs. Even object names and job titles can disclose customer information, so use non-sensitive identifiers where possible.

### What if the printer computer is offline?

PrintScaler queues jobs while the computer is offline and prints them when it reconnects, subject to configurable expiration.

Choose an expiration policy that matches the document. A delayed packing slip may still be useful; a kitchen ticket printed hours after service may create confusion.

Do not keep Lambda running while it waits for an offline workstation. Submit the job, then monitor its lifecycle separately.

## Common AWS Lambda printing failures

| Symptom | Likely cause | What to check |
|---|---|---|
| Lambda never runs | Notification or invocation permission is missing | Bucket prefix, suffix, event type, and function resource policy |
| S3 returns access denied | IAM, version access, or KMS permissions are incomplete | Execution role, bucket policy, object version, and encryption key |
| HTTPS request times out | No outbound route or a transient network problem | VPC routing, NAT configuration where applicable, and service reachability |
| API returns 401 | Missing or invalid credentials | API key value and Bearer header |
| Job appears but no page prints | Local delivery or printer problem | Agent connection, printer status, queue, and job history |
| Output has wrong dimensions | Document and printer settings disagree | PDF page size, media, and scaling |
| Multiple copies appear unexpectedly | Separate requests lack a stable business identity | Event keys, retry behavior, and independent producers |

For PDF layout problems, inspect the printer's available capabilities before adding options. PrintScaler supports settings such as paper size, tray, copies, duplex, DPI, and fit to page, but the selected values must suit the printer and document.

## When to change this architecture

The direct S3-to-Lambda approach is a practical starting point, not a complete workflow engine.

For bursts, consider placing SQS between S3 and Lambda. That gives you a buffer and more explicit concurrency control. PrintScaler's API rate limit is 10 requests per second with a burst of 30; uncontrolled Lambda scaling can exceed it. Use backoff for transient failures and rate-limit submissions across workers.

For larger PDFs, consider `pdf_uri` with an accessible HTTPS document URL. If using an S3 presigned URL, account for both its configured expiration and the lifetime of the credentials that signed it. Do not assume a short-lived link will remain usable throughout a delayed workflow.

For customer applications, also decide who authorizes printing and which printer each tenant may use. Keep API credentials on the backend rather than returning them to browsers or mobile clients.

PrintScaler pricing is per print request, not per page. The Free plan includes 100 prints per month and one computer, making it suitable for testing this flow; review the [current plans](https://printscaler.com/pricing) before sizing production usage. AWS execution, storage, and networking costs are separate.

## Start with one document and one printer

To print from AWS Lambda reliably, keep the responsibilities clear: S3 stores the document, Lambda submits the request, and the local agent reaches the printer. Stable idempotency keys, immutable inputs, and lifecycle monitoring turn that connection into a dependable workflow.

[Create a free PrintScaler account](https://printscaler.com/signup), connect one printer, and test a single PDF before enabling automatic printing for production documents.

## Frequently asked questions

### Can AWS Lambda print to a USB printer?

Yes, through a computer that can access the USB printer. Install the PrintScaler agent on that computer, then have Lambda submit an HTTPS print request using the printer's ID. Lambda does not access the USB connection directly.

### Do I need to open firewall ports to print from AWS Lambda?

Not with the PrintScaler agent-based workflow. The local agent uses an outbound WebSocket connection, so the printer does not need a public IP address or inbound port forwarding. Lambda still needs outbound HTTPS access to the API.

### Can Lambda print a PDF stored in a private S3 bucket?

Yes. Lambda can read the PDF with its IAM execution role and send it using the pdf_base64 content type, without making the object public. Another option is pdf_uri with an appropriately secured, accessible URL.

### How do I prevent duplicate printing when Lambda retries?

Use the same X-Idempotency-Key for retries of the same logical print request. Derive it from stable document or event identifiers and the destination printer, rather than generating a new random value on every invocation.

### What happens if the local printer computer is offline?

PrintScaler queues jobs while the computer is offline and prints them when it reconnects, provided they have not expired. Configure expiration to match the document's useful lifetime, and monitor job states rather than keeping Lambda running.

### Can AWS Lambda send ZPL labels instead of PDFs?

Yes. Send ZPL content using raw_base64 or raw_uri to a printer that understands ZPL. RAW submission does not convert PDF files into ZPL, so your application must supply the correct printer-language content.
