UniteLabs
Use Cases

Instrument Control

Control individual lab instruments directly with Python — without vendor GUIs or proprietary software.

Most lab instruments ship with proprietary software that ties you to a specific vendor's ecosystem. Scripting requires gaining access to closed APIs, fragile integrations, and endless vendor support tickets. UniteLabs replaces that with a clean Python interface to every connected device. We work together with the vendors to get there!

What this unlocks

  • Python-native control: call any instrument command from a workflow, script, or Jupyter notebook
  • No vendor GUI required: automate instruments that normally require manual interaction via the vendor software
  • Introspect available commands: discover what a device can do at runtime. Claude knows what your lab can do.
  • Async by default: non-blocking calls so your script can do other work while an instrument is busy. Don't worry, we also support a Sync client if you prefer.

How it works

Every instrument in UniteLabs is exposed as a connector: a standardized software interface that maps the device's native protocol to a uniform homogeneous integration layer that we expose with our UniteLabs SDK and REST API.

Once a connector is running and connected to your platform tenant, you can control the instrument from any Python script with network access:

control.py
import asyncio
from unitelabs.sdk import AsyncApiClient
from unitelabs.liquid_handling.hamilton import MicrolabSTAR
from unitelabs.labware import Standard96Plate
from unitelabs.labware.hamilton import PLT_CAR_L5MD_A00, TIP_CAR_480_A00, HamiltonTipRack_300, HamiltonTip_300_Filter

async def main():
    client = AsyncApiClient()

    # Connect to the liquid handler by name
    hamilton = MicrolabSTAR(name="Hamilton STAR", client=client)
    await hamilton.initialize()

    # Define the deck layout
    tip_carrier = TIP_CAR_480_A00()
    tip_carrier[0] = tip_rack_0 = HamiltonTipRack_300(filled_with=HamiltonTip_300_Filter())

    plate_carrier = PLT_CAR_L5MD_A00()
    plate_carrier[0] = plate_0 = Standard96Plate()

    hamilton.deck.add(tip_carrier, track=1)
    hamilton.deck.add(plate_carrier, track=3)

    # Pick up tips, aspirate from column 1, dispense into column 2
    await hamilton.pipettes.pick_up_tips_from(tip_rack_0)
    await hamilton.pipettes.aspirate(plate_0["A1:H1"], volume=50)
    await hamilton.pipettes.dispense(plate_0["A2:H2"], volume=50)
    await hamilton.pipettes.discard_tips()

asyncio.run(main())

The SDK generates the code for your instruments dynamically — if a new device is connected to the platform, it appears in list_services() without any implementation effort on your part.

When to use this

Low-level control is the right starting point when you want to:

  • Automate a single instrument that currently requires manual operation
  • Write a quick script or notebook to collect data from a device
  • Explore what a connector exposes before building a larger workflow
  • Integrate an instrument into an existing Python data pipeline

For coordinating multiple instruments together, see Multi-device Control. For fully tracked and reproducible runs, see Workflow Orchestration.

Next steps