Before you start
You need:- A Labelbox API key.
- Access to performance data. If you can open a project’s Performance dashboard, you can read that project through the API. Workspace reports need permission to view performance across your organization, as the Monitor does. Sign-ins and activity periods need permission to view all performance across your organization.
- For the Python examples, Python SDK version 7.13.0 or later. Member organizations (the
member_organization_idsfilter and themember_organization_idandmember_organization_namefields) and user groups on workspace reports need 7.14.0 or later. Sign-ins (get_sign_in_sessions()andget_sign_in_events()) need 7.15.0 or later.
GET requests under this base URL, authenticated with your API key as a bearer token:
Quick start
Get the number of labels created per day in a project:Endpoints
Every table is paged. Filters lists which parameters each report takes, Sign-in parameters those of the sign-in endpoints, and Activity parameters those of activity periods. See Paging and Reliability before you build on the API.
Units and dates
- Time is in seconds.
- Percentages and agreement scores are fractions from 0 to 1. A value of
0.9means 90%. - Dates are calendar days in UTC, written as
2026-01-31. Reports are bucketed by UTC day.startDateandendDateare the first and last day of the period, both included, and a request can cover at most 366 days, or 31 days for sign-ins and activity periods. - Times, such as
generatedAtand when a session started, are in UTC, written as2026-11-03T08:59:41.120Z. - Wider buckets keep their calendar bounds: a week runs from Monday, a month from the 1st. The first and last bucket of a series can reach beyond your period, but they count only the days inside it. For counts and time totals, a wider bucket is the sum of its days. Averages and percentages are worked out over the whole bucket, so they are not.
date, a datetime, or a string. A datetime with a time zone is converted to its UTC day.
Metrics
List the metrics available to you, with their units and definitions:Throughput
Efficiency
Average handling time includes creating, reviewing, and reworking time. The
aht_ metrics are listed and can be queried only when average handling time charts are enabled for your organization. Their buckets also carry modes, the handling time split into creating, reviewing, and reworking.
Quality
The two distribution metrics return score ranges instead of time intervals. Their response has
"bucketType": "score" and ten buckets, each with rangeStart, rangeEnd, and value, the number of labels whose score falls in that range. The ranges are fractions like the scores themselves: 0 to 0.1, up to 0.9 to 1.
Filters
These parameters narrow a report. Lists are comma-separated in a request, or a Python list in the SDK. The sign-in endpoints take their own: see Sign-in parameters.
Filters narrow each other: a report counts only the work that matches every filter you give. With
userIds and userGroupIds, for example, only users in both are counted. If nothing matches, every bucket is zero and every table is empty. It is never the unfiltered report. If you can only view your own performance, see Access and privacy.
User groups
A user group filter counts the work of the groups’ current members, whenever that work was done. A member who has left a group is no longer counted with it, and one who has joined is counted for the whole period.- On a project report, the groups are those attached to the project. A group that is empty, is not attached to the project, or that you cannot view matches no work.
- On a workspace report, the groups are your organization’s groups. A group of another organization, or one you cannot view, matches no work. You need permission to view user groups across your organization, and a restricted API key must include it.
- At most 5,000 members. If the groups you name have more than 5,000 members between them, the request is refused with
400. The count is of the groups’ current members, whatever the dates or other filters, so narrowing the report does not help. Name fewer groups in each request. A single group with more than 5,000 members cannot be used as a filter.
400 (userIds=), and the Python SDK raises ValueError for one (user_ids=[]). It is not read as “no filter”, which would report on everyone. To leave a filter off, leave the parameter out, or pass None in the SDK.
Labeler and reviewer totals
These are the two tables on the Performance dashboard: one row per member with their totals for the period.data and adds page, perPage, and total:
Labeler fields
The Python SDK uses the same names in snake case, such aslabels_created.
Reviewer fields
Download the report
Get a link to the project’s performance report as a file. This is the file the Download button on the Performance dashboard gives you.url and expiresInSeconds. The link works for 24 hours.
Workspace reports
Workspace reports are the Monitor’s data. They cover every project your organization owns and every project another organization shares with yours, or only the projects you name inprojectIds.
workspace/labelers returns one row for each labeler on each project they worked on in the period. A row has the labeler fields, without avgRating, plus:
The table is paged like the project tables. Without
sort, rows are ordered by project, then member.
Both workspace reports take a user group filter. Use it to report on a team, or on a vendor whose staff have accounts in your own organization:
user_group_ids takes IDs or UserGroup objects; see User groups to find a group.
A workspace response repeats projectIds, ownerOrganizationIds, memberOrganizationIds, memberType, and userGroupIds, or null for one you did not use. It does not repeat userIds, batchIds, or deletedLabels. In the SDK, a workspace metric has them as project_ids, owner_organization_ids, member_organization_ids, member_type, and user_group_ids. The result of get_workspace_labeler_performance() does not: it gives the rows, total, and cached_at, and a row’s member_type is that member’s own, not the filter.
Shared projects and workforce providers
A project belongs to one organization and can be shared with another, for example when you add a workforce provider to it. The provider’s members then work in your project, and the project appears in the provider’s workspace as a shared project. The API reflects both sides of that connection. What a workspace report covers. The projects your organization owns, and the projects shared with it. Two fields tell them apart on every row ofworkspace/labelers:
projectOrganizationIdis the organization that owns the project.memberTypeis whose member did the work:internalfor a member of your organization,externalfor a member of another organization or an Alignerr member. The Monitor calls external members Workforce members.
memberType is told from your side: to a workforce provider its own labelers are internal. Alignerr members are the exception: they are external to everyone, including to Alignerr’s own organization.
A third field, memberOrganizationId, is the organization the member’s account belongs to, so it tells one workforce provider from another. Both organization IDs are the same whoever asks: a customer’s project has the customer’s projectOrganizationId in the workforce provider’s report too.
Split a report. Use memberType and ownerOrganizationIds on either workspace report. These are the Monitor’s Member Type and Owned by filters. To report on one workforce provider, use memberOrganizationIds with the provider’s organization ID, as found in memberOrganizationId on the rows. If a vendor’s staff have accounts in your own organization, they are internal members of your organization: put them in a user group and filter by userGroupIds instead.
- A split covers members and projects with activity in the period. As on the Monitor,
memberType,memberOrganizationIds, andownerOrganizationIdsselect from the members and projects that have rows in the report for your dates and other filters. If none match, every bucket is zero. It is never the unfiltered report. - A split by owner leaves out projects that are gone. A project that can no longer be found has no owner, so
ownerOrganizationIdsnever selects it, and the splits by owner can add up to slightly less than the whole workspace. - A split by owner leaves out projects you cannot report on. Your workspace can include a project you have no role in. A metric split by
ownerOrganizationIdsdoes not count it, and lists it inskippedProjectIdsso that you know. - A split of a metric needs permission to view everyone’s performance. With
projectIds,memberType,memberOrganizationIds,ownerOrganizationIds, anduserGroupIdsonworkspace/metricsneed permission to view all performance in every project you name. Otherwise the request returns403. Onworkspace/labelersthey filter the rows you can already see. - Project reports carry
memberTypeandmemberOrganizationIdtoo. The labeler and reviewer tables of a project give each member’smemberType,memberOrganizationId, andmemberOrganizationName. Project reports do not takememberOrganizationIds. - Names and emails are masked between a customer and a workforce provider. See Access and privacy. IDs are never masked, so you can always group and filter by
userId,projectId,projectOrganizationId, andmemberOrganizationId. - User groups belong to one organization. On a project report, the groups are the project’s; a
userGroupIdsfilter on a project shared with you matches no work. On a workspace report, the groups are your own organization’s.
Sign-in sessions
Sign-in sessions show when your organization’s members signed in to Labelbox, and how and when each sign-in ended. Reading them needs permission to view all performance across your organization. The workspace-wide roles Admin, Read-only Admin, Project Lead, and Team Manager have it, and so does a custom role that includes it: see Access and privacy. A session is one sign-in in one browser, or a switch into your workspace from the same person’s user in another workspace. It covers every tab in that browser, the label editor included. Two browsers, or a private window, are two sessions. A session lasts until one of these ends it:- the member signs out;
- Labelbox records another end: a new sign-in in the same browser, a switch to another workspace, or the member’s removal;
- otherwise, the session reaches its maximum duration. That end is derived when you read the session.
workspace/sign-in-sessionslists the sessions that started in the period, one row per session.workspace/sign-in-eventslists the sign-ins and sign-outs in the period, in the order they happened. Use it for a timeline, or to keep another system up to date.
Sign-in parameters
A list that is sent must name at least one ID, as for the reports. Groups that add no members give no rows, never every member’s.
List sessions
data:
startDate is not listed, even if it was still open in the period. To see everything that happened in a period, read the events.
The response repeats userIds, userGroupIds, and status, or null for one you did not use. rules gives the lifetimes that expiresAt is worked out from: see Derived ends and the maximum duration. In the Python SDK, the result of get_sign_in_sessions() has neither: it gives only the rows.
Session fields
The Python SDK uses the same names in snake case, such asstarted_at.
A session is
OPEN while nothing has ended it and its expiresAt is still ahead. It could still be used. It does not mean the member is using Labelbox.
Sign-in and sign-out events
The events are aSIGN_IN when a session starts, at its startedAt, and a SIGN_OUT when it ends, at its endedAt, recorded or derived. They are ordered by occurredAt. The period applies to occurredAt, so a session that started before the period can sign out in it.
completeThrough, the earlier of the end of endDate and five minutes ago. Every event before it is listed. Events at or after it are not listed yet: they come in a later read. A derived sign-out is listed once its time is before completeThrough, so the feed never lists a sign-out ahead of time.
In the Python SDK, the result of get_sign_in_events() gives only the rows and complete_through, which is None until you have read the events to the end.
Event fields
The Python SDK uses the same names in snake case, such asoccurred_at.
Follow the feed
To keep a copy of the events up to date, read the feed again each time from where the last read stopped:- Ask for the events from the UTC day that holds one hour before the last
completeThroughyou stored, through today. - Drop the events whose
eventIdyou already have. - Store the new
completeThroughfor the next read.
eventId of every event in at least the 25 hours before the completeThrough you stored. The overlap also picks up an event that took unusually long to be recorded. A request covers at most 31 days, so after a longer gap, read the gap in parts.
How a session starts
How a session ends
Derived ends and the maximum duration
When nothing ends a session, it is reported as ended at itsexpiresAt, with endReason MAX_DURATION_REACHED and endDerived true. This end is derived when you read the session. It is never recorded, and nothing happens at that moment. By then every credential the sign-in issued had lapsed, so the member would have had to sign in again to go on.
expiresAt is worked out from the lifetimes in rules, given in seconds:
- 84 hours from sign-in. A session lasts at most the lifetime of the sign-in cookie (
cookieLifetimeSeconds, 302,400 seconds), plus a minute, from when the cookie was last set. The sign-in’s own token (signInTokenLifetimeSeconds, 24 hours) lapses before the cookie, so it does not change the end. - Later when extended. Opening a link to the workspace the member is already in, or connecting an application to their account, sets the cookie again and moves an open session’s
expiresAtlater. A session extended again and again has no fixed maximum. - 7 days after a workspace switch, where your organization uses bearer tokens. If your organization signs members in with tokens instead of cookies, a switch into your workspace issues a workspace token (
workspaceTokenLifetimeSeconds, 7 days), and the session can last as long as that token.
expiresAt is an upper bound. It assumes the browser kept the cookie: a member who cleared their cookies lost access earlier.
What is not recorded
Sessions record sign-ins and how they ended, not activity. These leave no record:- Closing the tab or the browser, a computer going to sleep, or a lost connection. The session stays open until something else ends it, or until its derived end.
- A session expiring. That is the derived end.
- The label editor’s idle sign-out when the editor is open in its own tab, as in queue labeling.
- A sign-out clicked after the session had expired. The derived end, which came first, stands.
- Impersonation. Nothing done while a Labelbox admin impersonates a member is recorded as that member’s.
- API keys. Requests made with an API key, such as through the Python SDK, are not sign-ins and start no session.
Active and idle periods
Activity periods show when your organization’s members were working in the label editor and when they were idle there, each with a start and an end. Together with sign-in sessions they give you the time to compare with your own shift schedule: time signed in, time active, and time idle. Reading them needs the same permission as sign-ins: to view all performance across your organization.- A member is active (
ACTIVE) while the editor’s timer is counting their time. - A member is idle (
IDLE) while the editor is open but its timer is paused. The usual reason is no input for five minutes (idleReasonINACTIVITY), when the project pauses the timer on inactivity. The editor shows “Inactivity detected” then, and the period ends at the next key press or click.idleReasonnames other reasons the timer can be paused for. - Time signed in but outside the editor, such as on a project’s overview, is neither. So signed-in time minus active and idle time is time spent elsewhere in Labelbox.
Activity parameters
List periods
cURL
data:
status OPEN and endedAt null.
Period fields
Why a period is idle
For idle time in the strict sense, filter on
INACTIVITY (and POSSIBLE_INACTIVITY where a time tracker is required).
How a period ends
What is recorded
Periods are recorded in the organization the member works in. A workforce provider’s members, or Alignerr accounts, working on your projects are recorded in their own organization, not yours. Nothing done while a Labelbox admin impersonates a member is recorded. Recording is being turned on organization by organization. Earlier time is not available, and periods are deleted 400 days after they started.Paging
The three tables (projects/{projectId}/labelers, projects/{projectId}/reviewers, and workspace/labelers) are paged by page number. In the Python SDK, iterating a table fetches one page at a time, and total gives the number of rows without reading them all:
Python
page and no total: see Paging by cursor.
In a request, use these parameters:
Every table response has
page, perPage, and total, the number of rows across all pages. Request pages until you have total rows. A page past the end returns an empty data.
cURL
- Leave the cache on. The pages of
workspace/labelersare cut from one reading of the report, kept for five minutes from when it was taken, so they agree with each other while it lasts and the rows are in a fixed order.cachedAtsays when that reading was taken. WithuseCache=falseevery page reads the report again, and so does a very large table, which is not kept. - A user group filter is applied to each page. On every table, who is in the groups is worked out again for each page. If someone joins or leaves one of the groups while you read the table, a row can be missed or repeated. If
totalchanges from one page to the next, start again from page 1. The Python SDK checks this for you: from 7.14.0, iterating raisesPerformanceTableChangedErrorwhen a page’stotaldiffers from the first page’s. A change that leavestotalas it was cannot be seen this way, such as a member joining a group as another with the same number of rows leaves. - Sorting. Every table sorts by the figures in its rows, such as
labelsCreatedoravgTimePerLabel, exceptavgRating. Rows cannot be sorted by member organization. Withoutsort, the project tables come ordered by email, as on the Performance dashboard, and rows that tie under a sort are in that order too. A masked email keeps the place of the real one.workspace/labelerscomes ordered by project, then member. - Metrics are not paged. A metric returns at most one bucket per day of the period, and the period is limited to 366 days.
Paging by cursor
workspace/sign-in-sessions, workspace/sign-in-events, and workspace/activity-periods are paged by cursor, not by page number. In the Python SDK, iterating fetches one page at a time, as for the tables:
Python
Every response has
nextCursor, or null on the last page. Request pages, with the same other parameters, until nextCursor is null. There is no page, no total, and no sort: sessions and activity periods come in the order they started, and events in the order they happened. A cursor is not meant to be read or built, and a value the endpoint did not return is refused with 400.
cURL
Access and privacy
The API shows you what the pages show you.- Your own performance only. If you can only view your own performance on a project, every project report returns only your data, whatever
userIdsoruserGroupIdsyou pass. The same goes for a workspace report when you name such a project inprojectIds: it counts only your own work, whateveruserIdsyou pass.workspace/metricsthen refusesuserGroupIds,memberType,memberOrganizationIds, andownerOrganizationIdswith403. - Workspace reports. Without
projectIds, you need permission to view performance across your organization. WithprojectIds, you need performance access to each project you name. - Projects you cannot see. A project you have no performance access to returns
404, the same as a project that does not exist. - Splitting by member type, organization, or user group. On
workspace/metrics,memberType,memberOrganizationIds,ownerOrganizationIds, anduserGroupIdsneed permission to view all performance in every project you name inprojectIds. A project found through its owner that you cannot report on is left out and listed inskippedProjectIds. - User groups. A
userGroupIdsfilter on a workspace report needs permission to view user groups across your organization. Without it, the groups match no one: the metric is zero and the table is empty, never the unfiltered report. If you can only view your own performance,workspace/labelersreturns your own rows, and withuserGroupIdsonly if you are in one of the groups. - Masked emails. The email of a member you may not identify is replaced with a masked value, as on the pages. For example, a workforce provider’s members appear masked to the project’s owner, and the owner’s members appear masked to the provider.
- Masked project and organization names. A workforce provider does not see the names of the projects shared with it or of the organizations that own them. In
workspace/labelers,projectNameandprojectOrganizationNameare replaced with masked values for those projects.memberOrganizationName, in all three tables, is masked by the same rules, asorg.name.<ID>: a workforce provider sees its customers’ organization names masked, and a customer sees the names of its workforce providers. The IDs are unchanged. - Sign-ins. The sign-in endpoints need permission to view all performance across your organization. Performance access to projects does not count, and neither does permission to view your own performance: a member cannot read their own sign-ins, and gets
403. Only sessions started in your organization are listed. Accounts of other organizations, such as a workforce provider’s members or Alignerr accounts, have none here, even when they work on your projects or are in your user groups. A member who has since moved to another organization keeps the sessions they started in yours, withemailnullandmemberDeletedtrue. - Activity periods. The same as sign-ins: they need permission to view all performance across your organization, a member cannot read their own, and only periods recorded in your organization are listed.
- Restricted API keys. A key created with a limited set of permissions needs a performance permission, and is limited to the ones it has. A key that only has permission to view your own performance returns only your own. For sign-ins and activity periods, such a key needs permission to view all performance, or it is refused with
403.
Data freshness
The API reads the same data as the Performance dashboard and the Monitor, so it is never more current than those pages. That data is rebuilt periodically, not continuously: the current day’s figures can trail real activity by anything from a quarter of an hour to over an hour. A response served from the cache can be up to five minutes older again.generatedAt is when the response was produced. cachedAt, on the labeler tables, is set when the rows came from a result computed in the last few minutes. Neither tells you how recent the underlying activity data is, and no response carries an as-of time for it.
Two things follow for the current day:
- Today’s bucket keeps growing. Read settled days for totals you will not revisit.
- Charts and tables do not refresh together. For settled days a metric and the labeler table normally add up to the same total. For the current day they can differ until both have caught up.
Limits
Requests count toward your API rate limit, like the rest of the Labelbox API.
Reliability
- Requests are safe to repeat. Every endpoint is a
GETand changes nothing, so you can retry any request. - Retry
5xxresponses and timeouts with a growing delay between attempts. The Python SDK does this for you, for up to four minutes, and tries once more after a request that timed out. When it stops retrying, it raises the error that kept happening. - Do not retry
4xxresponses without changing the request. For429, wait before sending more. - Allow for long requests. Reports are computed when you ask, and a long period over a large workspace can take a while. The Python SDK waits up to 120 seconds for a response.
- Prefer fewer, wider requests. One request for a month at
interval=daycosts less than a request for each day. To read every member’s totals for a project as a file, use Download the report. - Expect new fields. Responses can gain fields over time. Ignore the ones you do not know. The Python SDK does.
Errors
The Python SDK raises
TimeoutError when a request gets no response in time, and NetworkError when it cannot connect. All of these exceptions are in lbox.exceptions. It raises Python’s ValueError, before sending anything, for a filter given as an empty list.
A 400 response names each parameter that was refused: