The Python Protocol API has its own versioning system, which is separate from the versioning system used for the robot software and the Opentrons App. This allows protocols to run on newer robot software versions without modification.

Major and Minor Versions

The API uses a major and minor version number and does not use patch version numbers. For instance, major version 2 and minor version 0 is written as 2.0. Versions are not decimal numbers, so 2.10 indicates major version 2 and minor version 10. The Python Protocol API version will only increase based on changes that affect protocol behavior.

The major version of the API increases whenever there are significant structural or behavioral changes to protocols. For instance, major version 2 of the API was introduced because it required protocols to have a run function that takes a protocol argument rather than importing the robot, instruments, and labware modules. Protocols written with major version 1 of the API will not run without modification in major version 2. A similar level of structural change would require a major version 3. This documentation only deals with features found in major version 2 of the API; see the archived version 1 documentation for information on older protocols.

The minor version of the API increases whenever there is new functionality that might change the way a protocol is written, or when a behavior changes in one aspect of the API but does not affect all protocols. For instance, adding support for a new hardware module, adding new parameters for a function, or deprecating a feature would increase the minor version of the API.

Specifying Versions

You must specify the API version you are targeting in your Python protocol. In all minor versions, you can do this with the apiLevel key in the metadata dictionary, alongside any other metadata elements:

 from opentrons import protocol_api

 metadata = {
     "apiLevel": "2.18",
     "author": "A. Biologist"}

 def run(protocol: protocol_api.ProtocolContext):
     protocol.comment("Hello, world!")

From version 2.15 onward, you can specify apiLevel in the requirements dictionary instead:

 from opentrons import protocol_api

 metadata = {"author": "A. Biologist"}
 requirements = {"apiLevel": "2.18", "robotType": "Flex"}

 def run(protocol: protocol_api.ProtocolContext):
     protocol.comment("Hello, Flex!")

Choose only one of these places to specify apiLevel. If you put it in neither or both places, you will not be able to simulate or run your protocol.

The version you specify determines the features and behaviors available to your protocol. For example, support for the Heater-Shaker Module was added in version 2.13, so you can’t specify a lower version and then call HeaterShakerContext methods without causing an error. This protects you from accidentally using features not present in your specified API version, and keeps your protocol portable between API versions.

When choosing an API level, consider what features you need and how widely you plan to share your protocol. Throughout the Python Protocol API documentation, there are version statements indicating when elements (features, function calls, available properties, etc.) were introduced. Keep these in mind when specifying your protocol’s API version. Version statements look like this:

New in version 2.0.

On the one hand, using the highest available version will give your protocol access to all the latest features and fixes. On the other hand, using the lowest possible version lets the protocol work on a wider range of robot software versions. For example, a protocol that uses the Heater-Shaker and specifies version 2.13 of the API should work equally well on a robot running version 6.1.0 or 6.2.0 of the robot software. Specifying version 2.14 would limit the protocol to robots running 6.2.0 or higher.

Maximum Supported Versions

The maximum supported API version for your robot is listed in the Opentrons App under Robots > your robot > Robot Settings > Advanced. Before version 6.0.0 of the app, the same information was listed on your robot’s Information card.

If you upload a protocol that specifies a higher API level than the maximum supported, your robot won’t be able to analyze or run your protocol. You can increase the maximum supported version by updating your robot software and Opentrons App.

Opentrons robots running the latest software (7.3.0) support the following version ranges:

  • Flex: version 2.15–2.18.

  • OT-2: versions 2.0–2.18.

API and Robot Software Versions

This table lists the correspondence between Protocol API versions and robot software versions.

API Version

Introduced in Robot Software









































Changes in API Versions

Version 2.18

  • Define customizable parameters with the new add_parameters() function, and access their values on the ProtocolContext.params object during a protocol run. See Runtime Parameters and related pages for more information.

  • Move the pipette to positions relative to the top of a trash container. See Position Relative to Trash Containers. The default behavior of drop_tip() also accounts for this new possibility.

  • set_offset() has been restored to the API with new behavior that applies to labware type–location pairs.

  • Automatic tip tracking is now available for all nozzle configurations.

Version 2.17

Version 2.16

This version introduces new features for Flex and adds and improves methods for aspirating and dispensing. Note that when updating Flex protocols to version 2.16, you must load a trash container before dropping tips.

Version 2.15

This version introduces support for the Opentrons Flex robot, instruments, modules, and labware.

  • Flex features

    • Write protocols for Opentrons Flex by declaring "robotType": "Flex" in the new requirements dictionary. See the examples in the Tutorial.

    • load_instrument() supports loading Flex 1-, 8-, and 96-channel pipettes. See Loading Pipettes.

    • The new move_labware() method can move labware automatically using the Flex Gripper. You can also move labware manually on Flex.

    • load_module() supports loading the Magnetic Block.

    • The API does not enforce placement restrictions for the Heater-Shaker module on Flex, because it is installed below-deck in a module caddy. Pipetting restrictions are still in place when the Heater-Shaker is shaking or its labware latch is open.

    • The new configure_for_volume() method can place Flex 50 µL pipettes in a low-volume mode for dispensing very small volumes of liquid. See Volume Modes.

  • Flex and OT-2 features

    • Optionally specify apiLevel in the new requirements dictionary (otherwise, specify it in metadata).

    • Optionally specify "robotType": "OT-2" in requirements.

    • Use coordinates or numbers to specify deck slots. These formats match physical labels on Flex and OT-2, but you can use either system, regardless of robotType.

    • The new module context load_adapter() methods let you load adapters and labware separately on modules, and ProtocolContext.load_adapter() lets you load adapters directly in deck slots. See Loading Labware on Adapters.

    • Move labware manually using move_labware(), without having to stop your protocol.

    • Manual labware moves support moving to or from the new OFF_DECK location (outside of the robot).

    • ProtocolContext.load_labware() also accepts OFF_DECK as a location. This lets you prepare labware to be moved onto the deck later in a protocol.

    • The new push_out parameter of the dispense() method helps ensure that the pipette dispenses all of its liquid when working with very small volumes.

    • By default, repeated calls to drop_tip() cycle through multiple locations above the trash bin to prevent tips from stacking up.

  • Bug fixes

Version 2.14

This version introduces a new protocol runtime that offers more reliable run control and builds a strong foundation for future Protocol API improvements.

Several older parts of the Protocol API were deprecated as part of this switchover. If you specify an API version of 2.13 or lower, your protocols will continue to execute on the old runtime.

  • Feature additions

  • Bug fixes

    • Labware and Well now adhere to the protocol’s API level setting. Prior to this version, they incorrectly ignored the setting.

    • InstrumentContext.touch_tip() will end with the pipette tip in the center of the well instead of on the edge closest to the front of the machine.

    • ProtocolContext.load_labware() now prefers loading user-provided labware definitions rather than built-in definitions if no explicit namespace is specified.

    • ProtocolContext.pause() will now properly wait until you resume the protocol before moving on. In previous versions, the run will not pause until the first call to a different ProtocolContext method.

    • Motion planning has been improved to avoid certain erroneous downward movements, especially when using InstrumentContext.aspirate().

    • Labware.reset() and Labware.tip_length will raise useful errors if called on labware that is not a tip rack.

  • Removals

    • The presses and increment arguments of InstrumentContext.pick_up_tip() were deprecated. Configure your pipette pick-up settings with the Opentrons App, instead.

    • InstrumentContext.speed property was removed. This property tried to allow setting a pipette’s plunger speed in mm/s. However, it could only approximately set the plunger speed, because the plunger’s speed is a stepwise function of the volume. Use InstrumentContext.flow_rate to set the flow rate in µL/s, instead.

    • load_labware_object() was removed from module contexts as an unnecessary internal method.

    • geometry was removed from module contexts in favor of model and type attributes.

    • Well.geometry was removed as unnecessary.

    • MagneticModuleContext.calibrate was removed since it was never needed nor implemented.

    • The height parameter of MagneticModuleContext.engage() was removed. Use offset or height_from_base instead.

    • Labware.separate_calibration and Labware.set_calibration() were removed, since they were holdovers from a calibration system that no longer exists.

    • Various methods and setters were removed that could modify tip state outside of calls to InstrumentContext.pick_up_tip() and InstrumentContext.drop_tip(). This change allows the robot to track tip usage more completely and reliably. You may still use Labware.reset() and InstrumentContext.reset_tipracks() to reset your tip racks’ state.

      • The Well.has_tip setter was removed. The getter is still supported.

      • Internal methods Labware.use_tips, Labware.previous_tip, and Labware.return_tips were removed.

    • The configuration argument of ProtocolContext.load_module() was removed because it made unsafe modifications to the protocol’s geometry system, and the Thermocycler’s “semi” configuration is not officially supported.

  • Known limitations

Version 2.13

Version 2.12

Version 2.11

  • Attempting to aspirate from or dispense to tip racks will raise an error.

Version 2.10

  • Moving to the same well twice in a row with different pipettes no longer results in strange diagonal movements.

Version 2.9

  • You can now access certain geometry data regarding a labware’s well via a Well Object. See Well Dimensions for more information.

Version 2.8

  • You can now pass in a list of volumes to distribute and consolidate. See List of Volumes for more information.

    • Passing in a zero volume to any complex command will result in no actions taken for aspirate or dispense

  • Well.from_center_cartesian() can be used to find a point within a well using normalized distance from the center in each axis.

    • Note that you will need to create a location object to use this function in a protocol. See Labware for more information.

  • You can now pass in a blowout location to transfer, distribute, and consolidate with the blowout_location parameter. See InstrumentContext.transfer() for more detail!

Version 2.7

  • Added InstrumentContext.pair_with(), an experimental feature for moving both pipettes simultaneously.


    This feature has been removed from the Python Protocol API.

  • Calling InstrumentContext.has_tip() will return whether a particular instrument has a tip attached or not.

Version 2.6

  • GEN2 Single pipettes now default to flow rates equivalent to 10 mm/s plunger speeds

    • Protocols that manually configure pipette flow rates will be unaffected

    • For a comparison between API Versions, see OT-2 Pipette Flow Rates

Version 2.5

Version 2.4

  • The following improvements were made to the touch_tip command:

    • The speed for touch_tip can now be lowered down to 1 mm/s

    • touch_tip no longer moves diagonally from the X direction -> Y direction

    • Takes into account geometry of the deck and modules

Version 2.3

  • Magnetic Module GEN2 and Temperature Module GEN2 are now supported; you can load them with the names "magnetic module gen2" and "temperature module gen2", respectively.

  • All pipettes will return tips to tip racks from a higher position to avoid possible collisions.

  • During a mix(), the pipette will no longer move up to clear the liquid in between every dispense and following aspirate.

  • You can now access the Temperature Module’s status via TemperatureModuleContext.status.

Version 2.2

  • You should now specify Magnetic Module engage height using the height_from_base parameter, which specifies the height of the top of the magnet from the base of the labware. For more, see Engaging and Disengaging.

  • Return tip will now use pre-defined heights from hardware testing. For more information, see Returning a Tip.

  • When using the return tip function, tips are no longer added back into the tip tracker. For more information, see Returning a Tip.

Version 2.1

Version 2.0

Version 2 of the API is a new way to write Python protocols, with support for new modules like the Thermocycler. To transition your protocols from version 1 to version 2 of the API, follow this migration guide.

We’ve also published a more in-depth discussion of why we developed version 2 of the API and how it differs from version 1.