Define basic queries using OData Analytics

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019

Using Analytics for Azure DevOps, you can construct basic and filtered queries to return data of interest. You can run these queries directly in your browser or within Power BI.

This article builds off information provided in Construct OData queries for Analytics and Metadata reference for Azure Boards Analytics. Also, the queries in this article are focused on retrieving work tracking entity sets, however, the principles apply for querying other entity sets.

In this article you'll learn how to define queries that return the following data:

  • Count of items (no other data)
  • Count of items and data
  • Properties defined for Areas or Iteration Paths
  • Selected columns or fields
  • Filtered data
  • Return data for Identity, Area Path, and Iteration Path fields
  • Filter by a navigation property
  • Query a date range
  • Nest expand statements
  • Sort results, orderby option

Note

The Analytics service is automatically enabled and supported in production for all Azure DevOps Services. Power BI integration and access to the OData feed of the Analytics Service are generally available. We encourage you to use it and give us feedback. Available data is version-dependent. The latest supported version is v2.0, and the latest preview version is v4.0-preview. For more information, see OData API versioning.

Note

The Analytics service is automatically installed and supported in production for all new project collections for Azure DevOps Server 2020 and later versions. Power BI integration and access to the OData feed of the Analytics Service are generally available. We encourage you to use it and give us feedback. If you upgraded from Azure DevOps Server 2019, then you can install the Analytics service during upgrade.

Available data is version-dependent. The latest supported version is v2.0, and the latest preview version is v4.0-preview. For more information, see OData API versioning.

Note

The Analytics service is in preview for Azure DevOps Server 2019. You can enable or install it for a project collection. Power BI integration and access to the OData feed of the Analytics Service are in Preview. We encourage you to use it and give us feedback.

Available data is version-dependent. The latest supported version is v2.0, and the latest preview version is v4.0-preview. For more information, see OData API versioning.

Prerequisites

Note

In this article, the OData query URL is defined for Azure DevOps Services. To construct a similar query for an on-premises server, see the guidance provided in Construct OData queries for Analytics. We encourage you to adjust the queries provided for your organization and project to get familiar with querying OData using your browser.

Return a count of items (no other data)

To learn about the number of items or entities defined in an organization or project, specify $apply=aggregate($count as Count) query option. For example, the following queries return the number of projects, work items, Area Paths, and users defined for an organization.

https://analytics.dev.azure.com/{OrganizationName}/_odata/v4.0-preview/Projects?$apply=aggregate($count as Count)
https://analytics.dev.azure.com/{OrganizationName}/_odata/v4.0-preview/WorkItems?$apply=aggregate($count as Count)
https://analytics.dev.azure.com/{OrganizationName}/_odata/v4.0-preview/Areas?$apply=aggregate($count as Count)
https://analytics.dev.azure.com/{OrganizationName}/_odata/v4.0-preview/Users?$apply=aggregate($count as Count)

Tip

Cross-project queries fail when the user running the query doesn't have access to all the projects. Read more about requirements in Project and organization-scoped queries.

Project count

@odata.context	"https://analytics.dev.azure.com/fabrikam/_odata/v4.0-preview/$metadata#Projects(Count)"
value	
   0	
     @odata.id	null
     Count	    28

Work item count

@odata.context	"https://analytics.dev.azure.com/fabrikam/_odata/v4.0-preview/$metadata#WorkItems(Count)"
value	
  0	
     @odata.id	null
     Count	    1166

Area count

@odata.context	"https://analytics.dev.azure.com/fabrikam/_odata/v4.0-preview/$metadata#Areas(Count)"
value	
  0	
     @odata.id	null
     Count	    70

User count

@odata.context	"https://analytics.dev.azure.com/fabrikam/_odata/v4.0-preview/$metadata#Areas(Count)"
value	
  0	
     @odata.id	null
     Count	    16

Return a count of items and data

To return a count of items along with select data for the items, specify the $count=true query option. For example, the following queries return a count of work items, Area Paths, and users defined for a project along with the specified properties. For valid properties, see Metadata reference for Azure Boards Analytics and Calendar date, Project, and User metadata reference for Azure DevOps Analytics.

Tip

If you don't specify the properties to return, Analytics will return all properties defined for the specified entity type.

https://analytics.dev.azure.com/{OrganizationName}/{ProjectName}/_odata/v4.0-preview/WorkItems?$count=true&$select=WorkItemId,Title,WorkItemType 
https://analytics.dev.azure.com/{OrganizationName}/{ProjectName}/_odata/v4.0-preview/Areas?$count=true&$select=AreaName,AreaPath 
https://analytics.dev.azure.com/{OrganizationName}/{ProjectName}/_odata/v4.0-preview/Users?$count=true&$select=UserName,UserEmail

Areas or Iterations properties

To look up the AreaSK or IterationSK, or other property of an Area Path or Iteration Path, use the following queries.

Return the AreaSK for a specific Area Path

The following query specifies to return the AreaSK property defined for the Fabrikam Fiber\Service Delivery\Internet Area Path. To specify other properties defined for the Areas entity set, see Metadata reference for Azure Boards Analytics, Areas.

https://analytics.dev.azure.com/fabrikam/Fabrikam%20Fiber/_odata/v4.0-preview/Areas?$filter=AreaPath eq 'Fabrikam Fiber\Service Delivery\Internet' &$select=AreaSK

The query returns the following data.

{
  "@odata.context": "https://analytics.dev.azure.com/fabrikam/Fabrikam%20Fiber/_odata/v4.0-preview/$metadata#Areas(AreaSK)",
  "value": [
    {
      "AreaSK": "637dc203-0549-4415-9663-b6358ac27d21"
    }
  ]
}

Return the IterationSK for a specific Iteration Path

The following query specifies to return the IterationSK property defined for the Fabrikam Fiber\Release 1\Sprint 3 Iteration Path. To specify other properties defined for the Iterations entity set, see Metadata reference for Azure Boards Analytics, Iterations.

https://analytics.dev.azure.com/fabrikam/Fabrikam%20Fiber/_odata/v4.0-preview/Iterations?$filter=IterationPath eq 'Fabrikam Fiber\Release 1\Sprint 3' &$select=IterationSK

The query returns the following data.

{
  "@odata.context": "https://analytics.dev.azure.com/fabrikam/Fabrikam%20Fiber/_odata/v4.0-preview/$metadata#Iterations(IterationSK)",
  "value": [
    {
      "IterationSK": "862e961a-ac7a-4fcc-9ebc-8afd0c12fed5"
    }
  ]
}

Return specific properties or fields

To return specific properties or work item fields, add a $select clause that specifies the property names.

For example, to return the Work Item ID, Work Item Type, Title, and State of work items, add the following clause to your query. This clause specifies the properties that correspond to the named fields.

Note

Property names don't contain any spaces. Your query will fail if you add spaces. OData queries require attention is paid to both spacing and casing. To understand how custom field properties are labeled, see Metadata reference for Azure Boards, Custom properties.

Here we specify to return the top three work items.

https://analytics.dev.azure.com/fabrikam/Fabrikam Fiber/_odata/v4.0-preview/WorkItems?$select=WorkItemId,WorkItemType,Title,State&$top=3

Analytics returns the following data.

@odata.context	"https://analytics.dev.azure.com/fabrikam/Fabrikam%20Fiber/_odata/v4.0-preview/$metadata#WorkItems(WorkItemId,WorkItemType,Title,State)"
value	
   0	
      WorkItemId    462
      Title         "Test case"
      WorkItemType  "Test Case"
      State         "Design"
   1	
      WorkItemId	   491
      Title          "Change color settings"
      WorkItemType   "Shared Steps"
      State          "Active"
   2	
      WorkItemId	   461
      Title          "Test impediment"
      WorkItemType   "Impediment"
      State          "Open"

.

Filter your data

To filter an entity set to return select items, specify a $filter clause that specifies the criteria the items must meet. Building on the last query, here we add a filter clause to only return Feature work item types that are in the In Progress state.

/WorkItems?$select=WorkItemId,WorkItemType,Title,State&$filter=State eq 'In Progress'

https://analytics.dev.azure.com/fabrikam/Fabrikam Fiber/_odata/v4.0-preview/WorkItems?$filter=WorkItemType eq 'Feature' and State eq 'In Progress'&$select=WorkItemId,Title,AssignedTo,State

Analytics returns the following data.

  
@odata.context	"https://analytics.dev.azure.com/kelliott/Fabrikam%20Fiber/_odata/v4.0-preview/$metadata#WorkItems(WorkItemId,Title,State)"
value	
  0	
      WorkItemId	380
      Title	"Refresh web look, feel, performance factors"
      State	"In Progress"
  1	
      WorkItemId	480
      Title	"Customer Phone - Phase 1"
      State	"In Progress"
  2	
      WorkItemId	493
      Title	"Change initial view"
      State	"In Progress"
  3	
      WorkItemId	479
      Title	"Customer Web - Phase 1"
      State	"In Progress"
  4	
      WorkItemId	551
      Title	"Mobile feedback"
      State	"In Progress"

Specify several filter clauses

You can use AND and OR to specify several filter clauses.

For example, the following query specifies to return work items of type User Story, Bug, and Backlog Work (a custom work item type) that are in the New, Committed, or Active states. Use parenthesis to group filter clauses as needed.

Additionally, you can apply various functions such as contains, startswith, endswith and more. See the Supported OData features and clauses, Supported functions.

Return data for Identity, Area Path, and Iteration Path fields

Select properties are associated with navigational properties and aren't directly accessible using the $select statement. You must use an $expand statement to return the data of interest. These properties are often associated with several properties of their own. For example, with Identity fields, you can specify to return the user name or the user email.

The following table provides examples of how to expand several of these properties.

Type fields Referenced property Example clauses to include
DateTime DateSK $expand=CreatedDate($select=Date) or
$expand=CreatedDate($select=WeekStartingDate)
Identity UserSK $expand=AssignedTo($select=UserName) or
$expand=AssignedTo($select=UserEmail)
Area AreaSK $expand=Area($select=AreaName) or
$expand=Area($select=AreaPath)
Iteration IterationSK $expand=Iteration($select=IterationName) or
$expand=Iteration($select=IterationPath) or
$expand=Iteration($select=StartDate)
Project ProjectSK $expand=Project($select=ProjectName)
Team TeamSK $expand=Teams($select=TeamName)

To specify several properties that need to be expanded, you specify them in a single expand clause within a comman-delimited list.

$expand=AssignedTo($select=UserName),Iteration($select=IterationPath),Area($select=AreaPath)

Filter by a navigation property

When you specify a navigation property as part of your filter criteria, you must specify it in the required format.

For example, the following clause specifies to filter work items based on Iteration 1 defined for the project.

/WorkItems?$filter=Iteration/IterationPath eq 'Project Name\Iteration 1'

In this example, Iteration is the navigation property name and IterationPath corresponds to the full path for the iteration. To use another entity as a filter, put the navigation property followed by a slash followed by the name of the field to filter on.

And, here's the full OData query:

https://analytics.dev.azure.com/{OrganizationName}/{ProjectName}/_odata/{version}/WorkItems?$filter=Iteration/IterationPath eq 'Project Name\Iteration 1'

Here's another example that requests the top five work items under the Fabrikam Fiber\Service Delivery\Voice Area Path are returned.

https://analytics.dev.azure.com/fabrikam/Fabrikam%20Fiber/_odata/v4.0-preview/WorkItems?$top=5&$filter=Area/AreaPath eq 'Fabrikam Fiber\Service Delivery\Voice'&$select=WorkItemId, WorkItemType, Title, State&$orderby=WorkItemId asc

@odata.context	"https://analytics.dev.azure.com/fabrikam/Fabrikam%20Fiber/_odata/v4.0-preview/$metadata#WorkItems(WorkItemId,WorkItemType,Title,State)"
value	
  0	
     WorkItemId	361
     Title        "Hello World Web Site"
     WorkItemType	"Product Backlog Item"
     State        "Removed"
  1	
     WorkItemId	362
     Title        "Resume"
     WorkItemType	"Product Backlog Item"
     State        "New"
  2	
     WorkItemId	363
     Title        "Welcome back page"
     WorkItemType	"Product Backlog Item"
     State        "Done"
  3	
     WorkItemId	365
     Title        "Pause"
     WorkItemType	"Feature"
     State        "New"
  4	
     WorkItemId	374
     Title        "Fix performance issues"
     WorkItemType	"Task"
     State        "To Do"

Tip

You can't use the navigation property directly in a $select statement. Instead, you need to use $expand.

The previous filtering example for the Iteration Path doesn't return the iteration path because it's contained in a related entity. To return data in a related entity, add an $expand statement:

/WorkItems?$select=WorkItemId,WorkItemType,Title,State&$filter=WorkItemId eq 10000&$expand=Iteration

And here's an example that returns information assigned to work item ID 480.

https://analytics.dev.azure.com/fabrikam/Fabrikam%20Fiber/_odata/v4.0-preview/WorkItems?$filter=WorkItemId eq 480&$select=WorkItemId,WorkItemType,Title,State&&$expand=Iteration
   
@odata.context	"https://analytics.dev.azure.com/fabrikam/Fabrikam%20Fiber/_odata/v4.0-preview/$metadata#WorkItems(WorkItemId,WorkItemType,Title,State,Iteration)"
value	
  0	
      WorkItemId           480
      Title                "Customer Phone - Phase 1"
      WorkItemType	       "Feature"
      State	               "In Progress"
      Iteration	
          ProjectSK	       "56af920d-393b-4236-9a07-24439ccaa85c"
          IterationSK	       "c7063041-ff3a-4d7f-bb46-c433c7030d59"
          IterationId	       "c7063041-ff3a-4d7f-bb46-c433c7030d59"
          IterationName	   "Sprint 1"
          Number	            55297
          IterationPath	    "Fabrikam Fiber\\Release 1\\Sprint 1"
          StartDate	        "2022-01-17T00:00:00-08:00"
          EndDate	            "2022-02-04T23:59:59.999-08:00"
          IterationLevel1	    "Fabrikam Fiber"
          IterationLevel2	    "Release 1"
          IterationLevel3	    "Sprint 1"
          IterationLevel4	    null
          IterationLevel5	    null
          IterationLevel6	    null
          IterationLevel7	    null
          IterationLevel8	    null
          IterationLevel9	    null
          IterationLevel10	null
          IterationLevel11	null
          IterationLevel12	null
          IterationLevel13	null
          IterationLevel14	null
          Depth	            2
          IsEnded	        	true
      AnalyticsUpdatedDate	"2022-01-18T22:18:58.17Z"

As you can see, the Iteration Path is expanded in the result and all of the iteration data is returned. It's probably more data than you want.

To return less data, add a $select statement against the iteration as well:

/WorkItems?$select=WorkItemId,WorkItemType,Title,State&$filter=WorkItemId eq 10000&$expand=Iteration($select=Name,IterationPath)

It then returns the following data.

{
  "@odata.context":"https://analytics.dev.azure.com/{OrganizationName}/{ProjectName}/_odata/{version}/$metadata#WorkItems(WorkItemId,WorkItemType,Title,State,Iteration,Iteration(Name,IterationPath))",
  "value":[
    {
      "WorkItemId":10000,
      "WorkItemType":"Task",
      "Title":"Some title",
      "State":"Completed",
      "Iteration":{
        "Name":"Sprint 55",
        "IterationPath":"Fabrikam\\Sprints\\Sprint 55"
      }
    }
  ]
}

Query a date range

The following example returns work items whose Changed Date is greater than equal to January 1, 2021.

https://analytics.dev.azure.com/{OrganizationName}/{ProjectName}/_odata/{version}/WorkItems?$select=WorkItemId,WorkItemType,Title,State&$filter=ChangedDate ge 2021-01-01Z

The following example returns work items whose Changed Date occurs during the week of April 26 through April 30, 2021.

https://analytics.dev.azure.com{OrganizationName}/{ProjectName}/_odata/{version}/WorkItems?$select=WorkItemId,WorkItemType,Title,State&$filter=ChangedDate ge 2021-04-26Z&ChangedDate le 2021-04-30Z

Nest expand statements

In OData, you can nest $expand statements. For example, you can write the previous query statement to display the project the iteration is part of:

/WorkItems?$filter=WorkItemId eq 10000&$expand=Iteration($expand=Project)

It returns the following JSON:

{
  "@odata.context":"https://analytics.dev.azure.com/{OrganizationName}/{ProjectName}/_odata/{version}/$metadata#WorkItems",
  "value":[
    {
      "WorkItemId":10000,
      "Revision":3,
      "Watermark":283397,
      "Title":"Production deployment and testing for Entitlement API v2 and Subscriber database",
      "WorkItemType":"Task",
      "ChangedDate":"2014-07-10T19:29:58.41Z",
      "CreatedDate":"2014-04-19T22:44:58.31Z",
      "State":"Completed",
      "Reason":"Completed",
      "Priority":2,
      "CompletedWork":10.0,
      "OriginalEstimate":20.0,
      "Count":1,
      "Iteration":{
        "IterationId":"7a2c246e-fc62-41af-ad18-62332017bc46",
        "Name":"Sprint 55",
        "Number":13021,
        "IterationPath":"Fabrikam\\Sprints\\Sprint 55",
        "StartDate":"2013-09-23T00:00:00Z",
        "EndDate":"2013-10-11T00:00:00Z",
        "IterationLevel1":"Fabrikam",
        "IterationLevel2":" Sprints",
        "IterationLevel3":"Sprint 55",
        "Level":2,
        "IsDeleted":false,
        "Project":{
          "ProjectId":"b924d696-3eae-4116-8443-9a18392d8544",
          "ProjectName":"Fabrikam",
          "IsDeleted":false
        }
      }
    }
  ]
}

You can also combine $expand and $select statements. For example, you can change the previous query to only return the Iteration Name and Iteration Path:

/WorkItems?$filter=WorkItemId eq 10000&$expand=Iteration($select=IterationId,IterationPath;$expand=Project)

It returns the following JSON:

{
  "@odata.context":"https://analytics.dev.azure.com/{OrganizationName}/{ProjectName}/_odata/{version}/$metadata#WorkItems(Iteration(IterationId,IterationPath,Project))",
  "value":[
    {
      "WorkItemId":10000,
      "Revision":3,
      "Watermark":283397,
      "Title":"Production deployment and testing for Entitlement API v2 and Subscriber database","WorkItemType":"Task",
      "ChangedDate":"2014-07-10T19:29:58.41Z",
      "CreatedDate":"2014-04-19T22:44:58.31Z",
      "State":"Completed",
      "Reason":"Completed",
      "Priority":2,
      "CompletedWork":10.0,
      "OriginalEstimate":20.0,
      "Count":1,
      "Iteration":{
        "IterationId":"7a2c246e-fc62-41af-ad18-62332017bc46","IterationPath":"Fabrikam\\Sprints\\Sprint 55",
        "Project":{
          "ProjectId":"b924d696-3eae-4116-8443-9a18392d8544",
          "ProjectName":"Fabrikam",
          "IsDeleted":false
        }
      }
    }
  ]
}

Notice that the result here shows only the IterationId and IterationPath and that the Project is a nested object within the JSON result. Another key item to note is the URL itself. When using a $select statement and an $expand clause, you must use a semi-colon (;) before the $expand. Anything else will result in an error.

Sort results, orderby option

Specify the $orderby option to sort your results or specify the sequence in which results are returned. You can sort in ascending or descending order using keywords asc or desc, respectively. Some examples are shown

Sort by Clause to include
Work item ID /WorkItems?$orderby=WorkItemId
Work item ID descending /WorkItems?$orderby=WorkItemId desc
Work item type and State /WorkItems?$orderby=WorkItemType,State

Next steps