---
title: How to Upload Binary Data Through the Notecard with Python SDK
description: Learn how to rapidly upload binary data to a remote server with Notecard and Notehub using the note-python SDK.
source_url: https://dev.blues.io/blog/uploading-binary-files-with-notecard-python/
canonical_url: https://dev.blues.io/blog/uploading-binary-files-with-notecard-python/
markdown_url: https://dev.blues.io/blog/uploading-binary-files-with-notecard-python.md
---

# How to Upload Binary Data Through the Notecard with Python SDK

![How to Upload Binary Data Through the Notecard with Python SDK banner](https://dev.blues.io/images/blog/posts/uploading-binary-files-with-notecard-python/banner.png?v=e328625c)

February 18, 2026

## Learn how to rapidly upload binary data to a remote server with Notecard and Notehub using the note-python SDK.

- [Notecard](https://dev.blues.io/blog/tag/notecard)
- [Python](https://dev.blues.io/blog/tag/python)
- [Notehub](https://dev.blues.io/blog/tag/notehub)

[![Alex Bucknall](https://dev.blues.io/images/blog/authors/alex-bucknall.jpeg?v=92d47efd)](https://dev.blues.io/blog/author/alex-bucknall/)

[Alex BucknallStaff Developer Experience Engineer](https://dev.blues.io/blog/author/alex-bucknall/)

Notecard is great at sending telemetry to the cloud, but what about when you need to send something bigger — like an image, audio recording, or a log file? In this article I'll walk through a new addition to the [note-python SDK](https://github.com/blues/note-python) that makes uploading binary data with Notecard easy.

## Why Binary Uploads?

Most IoT projects send small JSON payloads: a temperature reading, a GPS coordinate, a status update. But there are plenty of scenarios where you need to move larger binary data from a device:

- **Camera images** from a wildlife monitor or security camera
- **Data logs** captured over days or weeks
- **Audio recordings** from an environmental sensor
- **File captures** from any sort of sensor (e.g. a video camera)

Notecard has a built-in binary buffer that can stage raw binary data before sending it to [Notehub](https://notehub.io) via a `web.post` request. The new `upload` function in note-python handles all the complexity of chunking, encoding, verification, and retry logic for you.

> **Note:**
>
> Sending binary data, as shown in this article, will rapidly consume your data plan. It can also be power intensive, so you should consider how frequently you need to send binary data.

## How It Works

At a high level, the upload process works like this:

1. **Query the Notecard's buffer capacity** — The SDK asks Notecard how much binary data it can hold at once via `card.binary`.
2. **Chunk the data** — If the file is larger than the buffer, it's split into chunks that fit.
3. **Stage each chunk** — Each chunk is [COBS-encoded](https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing) (so binary data can safely travel over the serial connection) and written into Notecard's binary buffer via `card.binary.put`.
4. **Verify the transfer** — After staging, the SDK checks `card.binary` to confirm the data arrived intact.
5. **Send to Notehub** — A `web.post` request with `binary: true` tells Notecard to forward the staged binary data to a [Notehub proxy route](https://dev.blues.io/notecard/notecard-walkthrough/web-transactions.md). The `offset` and `total` fields let Notehub reassemble multi-chunk uploads.
6. **Retry on failure** — Both the binary staging step and the `web.post` step have built-in retry logic with backoff.

Let's look at how to use it in practice.

## Setting Up a Notehub Route

Before uploading, you need somewhere for the data to go. In [Notehub](https://notehub.io), create a **Proxy for Notecard Web Requests** proxy route that points to your server:

1. Navigate to your project's **Routes** page.
2. Create a new **Proxy for Notecard Web Requests** route.
3. Set the **URL** to your server's endpoint (during development, [ngrok](https://ngrok.com/) is a great way to expose a local server).
4. Give it a **Alias** (e.g. `upload`) — you'll reference this in code.

![Notehub route](https://dev.blues.io/images/blog/posts/uploading-binary-files-with-notecard-python/notehub-route.png?v=75ec4740)

## Uploading a File with Python

Here's a complete example that uploads a PNG image through the Notecard:

```python
import serial
import notecard
from notecard import hub
from notecard.upload import upload

# Connect to Notecard over serial
port = serial.Serial('/dev/ttyUSB0', 115200)
card = notecard.OpenSerial(port)

# Connect Notecard to Notehub
hub.set(card, product='com.your-company:your-product', mode='continuous')

# Read the file to upload
with open('photo.png', 'rb') as f:
    data = f.read()

# Upload it
result = upload(
    card,
    data,
    route='upload',
    label='photo.png',
    content_type='image/png',
)

print(f'Uploaded {result["bytes_uploaded"]} bytes '
      f'in {result["duration_secs"]:.1f}s '
      f'({result["bytes_per_sec"]:.0f} B/s)')
```

The `upload` function handles chunking, COBS encoding, MD5 verification, and retries automatically.

## Setting Chunk Size

By default the SDK uses the full capacity of Notecard's binary buffer (\~250 KB) as the chunk size. This is optimal when you're connected over USB serial, but over cellular you may want smaller chunks to avoid timeouts on the `web.post` request:

```python
result = upload(
    card,
    data,
    route='upload',
    label='photo.png',
    max_chunk_size=65536,  # 64 KB chunks
)
```

A chunk size of 64 KB is a good starting point for cellular connections. If you're on a slow network, try 32 KB.

## Tracking Progress

For larger files, you may want to show upload progress. The `progress_cb` parameter takes a callback that fires after each chunk:

```python
def on_progress(info):
    print(f'  Chunk {info["chunk"]}/{info["total_chunks"]} '
          f'- {info["percent_complete"]:.1f}% '
          f'- {info["avg_bytes_per_sec"]:.0f} B/s '
          f'- ETA {info["eta_secs"]:.0f}s')

result = upload(
    card,
    data,
    route='upload',
    label='firmware.bin',
    max_chunk_size=65536,
    progress_cb=on_progress,
)
```

The callback receives a dictionary with `chunk`, `total_chunks`, `bytes_sent`, `total_bytes`, `percent_complete`, `bytes_per_sec`, `avg_bytes_per_sec`, and `eta_secs`.

## Receiving the Upload

On the server side, Notehub forwards the binary data as a standard HTTP POST to whatever URL you configured in your proxy route. You can use any HTTP framework to receive it. The note-python repo includes a [minimal Python receive server](https://github.com/blues/note-python/blob/main/examples/binary-mode/upload/receive_binary.py) that saves incoming files to disk:

```bash
python receive_binary.py
```

This listens on port 8000 and saves files to the current directory, using the `X-Notecard-Label` header (set from the `label` field in `web.post`) as the filename.

### Exposing your server with ngrok

Notehub routes need a publicly reachable URL, so if you're running the receive server on your local machine you'll need a tunnel. [ngrok](https://ngrok.com/) is the easiest way to do this during development.

In a separate terminal, run:

```bash
ngrok http 8000
```

This gives you a public HTTPS URL (e.g. `https://abc123.ngrok-free.app`) that tunnels traffic to your local server. Copy that URL and paste it into your Notehub route's **URL** field. Now when the Notecard sends a `web.post`, Notehub forwards it to ngrok, which forwards it to your local `receive_binary.py` — no cloud deployment needed.

### Where you can send your data

The ngrok + local server setup is great for testing, but in production you'll want a permanent destination. Because Notehub routes are just standard HTTP requests, you can point them at almost any cloud service that accepts file uploads:

- **[Cloudflare Workers](https://workers.cloudflare.com/)** — Write a lightweight Worker that receives the POST and stores the file in [R2](https://developers.cloudflare.com/r2/) (Cloudflare's S3-compatible object storage). No servers to manage and generous free tier.
- **[AWS S3](https://aws.amazon.com/s3/)** — Use an [API Gateway](https://aws.amazon.com/api-gateway/) + [Lambda](https://aws.amazon.com/lambda/) function to receive the upload and write it to an S3 bucket.
- **[Google Cloud Storage](https://cloud.google.com/storage)** — A [Cloud Function](https://cloud.google.com/functions) triggered by an HTTP request can write directly to a GCS bucket.
- **[Azure Blob Storage](https://azure.microsoft.com/en-us/products/storage/blobs/)** — An [Azure Function](https://azure.microsoft.com/en-us/products/functions/) can receive the POST and store the binary in a blob container.
- **Your own server** — Any web framework (Flask, Express, FastAPI, etc.) running on a VPS or container platform can receive the upload. The `receive_binary.py` script in the repo is a good starting point.

In each case, the setup is the same: deploy your endpoint, set its URL as the Notehub proxy route, and the upload function handles the rest.

## Behind the scenes

If you're curious about the implementation details, here's what happens for each chunk:

1. The SDK calls `card.binary.put` with the COBS-encoded size, then transmits the raw encoded bytes directly over the serial or I2C connection. This bypasses the normal JSON request/response flow for maximum throughput.

2. After the transfer, `card.binary` is called to verify the decoded length matches what was sent. If there's a mismatch or a `{bad-bin}` error, the SDK retries automatically.

3. Once verified, `web.post` is issued with `binary: true`, which tells Notecard to send the contents of its binary buffer to the specified Notehub route. The `offset`, `total`, and `status` (MD5 hash) fields allow Notehub to reassemble and verify multi-chunk uploads.

4. If the `web.post` fails (network error or HTTP status >= 300), the SDK waits 15 seconds, re-stages the binary data, and retries.

## Using note-cli

If you don't want to use the Python SDK, you can also use the [note-cli](https://dev.blues.io/tools-and-sdks/notecard-cli.md) tool to upload binary data using the same mechanism.

```bash
note-cli upload --file <path-to-file> --route <route-name>
```

> **Note:**
>
> You'll need at least version 1.9.6 of the note-cli tool to use this feature.

## Get Started

The upload functionality is available in the latest version of note-python. To try it out:

1. Install or update the SDK: `pip install note-python`
2. Check out the [complete upload example](https://github.com/blues/note-python/tree/main/examples/binary-mode/upload) in the repo
3. Read the [web request guide](https://dev.blues.io/notecard/notecard-walkthrough/web-transactions.md) for more on Notecard web transactions

If you have questions or run into issues, let us know on the [Blues community forum](https://discuss.blues.com).
