Skip to main content
A label changes as it moves through a workflow: a labeler submits it, a reviewer edits it, approves or rejects it, and a rework step changes it again. Review and rework edit the same label, so the label you read today shows only its last state. Label version history keeps a version of the label at each of these hand-offs: who made it, when, in which workflow step, and the label’s annotations at that moment. Compare consecutive versions of a label to see what a reviewer or a rework changed.

Before you start

You need:
  • Label version history enabled for your projects. Contact your Labelbox team to enable label version history. Versions are recorded from then on: there are none from before it was enabled.
  • Permission to read the project’s labels. See Who can read versions.
  • For the Python examples, Python SDK version 7.16.0 or later.

What is recorded

A version is recorded for each label at each of these hand-offs: A review decision on single labels records a version of each label it names. A decision on the whole data row, a rework submit, and a move into a rework step record a version of every label on the data row. Each version holds the label’s annotations as they were committed by that hand-off, with the feature names in effect then. A reviewer who edits a label and then approves it records two versions: SAVED, then APPROVED. If the decision is recorded while the reviewer’s save is still being committed, the decision’s version can show the annotations from before that save. The save is then the next version.

What is not recorded

  • Autosaves and drafts. Only the hand-offs above record a version. Edits in the editor that were not saved, and skipped data rows, record none.
  • Earlier history. Nothing from before label version history was enabled for the project.
  • Other changes to labels. Labels imported through the SDK or API, and reviews of tasks outside the label editor, such as external tasks, record no version.
  • Very large labels. A label whose annotations take more than 64 MB, or more than 16 MB compressed, is not recorded. Recording never holds up the hand-off: if a version cannot be recorded, the hand-off goes ahead without it.
Versions are never changed, and they stay readable after their label is deleted. They are erased with the data row’s labels when the data row is permanently erased.

Who can read versions

You can read the versions of the labels you can read in the project, as for the project’s labels:
  • Without permission to view other users’ labels, you see only the versions of labels you created.
  • In another organization’s project, without permission to view all labels, you see only the versions of labels your organization’s members created.
  • Otherwise, you see the versions of all the project’s labels.
Visibility follows who created the label, not who made the hand-off. Without permission to read any of the project’s labels, a list of versions is refused.

Read versions with the Python SDK

List one label’s versions, oldest first:
Python
version.label_json holds the annotations at that moment. For a Label you already have, such as one from project.labels(), label.versions() lists the same versions. List the versions of a whole project, filtered by data row, user, or time:
Python
Project.label_versions() takes these parameters, all optional: Label.versions() and Client.get_label_version() take include_annotations too. Client.get_label_version() returns None when the version does not exist or you cannot read its label. The SDK raises ValueError, before sending anything, for a page_size outside 1 to 100 or a created_after that is not before created_before. It raises lbox.exceptions.AuthorizationError when you cannot read the project’s labels.

Version fields

Because label has the same shape as Label.label, you can compare a version with the label as it is now, or with the version before it, using the same code.

Read versions with the GraphQL API

Labelbox recommends the Python SDK. GraphQL APIs may change without notice: see GraphQL API.
The labelVersions query lists a project’s versions, oldest first:
GraphQL
Send it to https://api.labelbox.com/graphql with your API key as a bearer token. where takes projectId, which is required, and optionally labelId, dataRowId, createdById, createdAfter (included), and createdBefore (excluded). first is the page size, from 1 to 100, and 100 by default. To read the next page, send the same query again with after set to the previous page’s nextCursor, until nextCursor is null. A cursor only continues the query it came from: with other filters, another project, or another organization it is refused. labelVersion(where: { id: "<VERSION_ID>" }) returns one version, or null when it does not exist or you cannot read its label.

Reading recent versions

Versions are listed in the order they were recorded. A version is listed once its hand-off has committed, so a hand-off still committing can appear a moment later with an earlier createdAt. To read new versions as they come, read up to a time at least a minute in the past with createdBefore, and start the next read from there with createdAfter.

Labelbox MCP server

Through the Labelbox MCP server, an AI assistant can audit a label’s history for you:
  • list_label_versions lists a project’s versions, oldest first, with the same filters as the API: labelId, dataRowId, createdById, createdAfter, and createdBefore. It returns the versions’ metadata, not their annotations.
  • get_label_version reads one version, with a summary of its annotations at that moment: their names and text answers, for up to 200 annotations. It never returns geometry, masks, or the full label. Read those with the SDK or the API.
The labelbox://guides/label-history guide shows the assistant how to find a label, page its versions, and compare them. Ask, for example:
The tools need a connection that can read project quality data, and the same permissions to read labels as the API. Neither tool edits a label or restores a version.