🔎 Exploring Teamcity API (with Examples!)
Tags: teamcity, api, postman, webdev
Published: 2021-02-25 16:36:37

I've been working on a side project 'DevOps API Tool' where I am essentially combining Bitbucket API, Octopus Deploy API and Teamcity API to get the most useful and common 'things' out of it into a simplified couple of webpages. As well as a number of reasons including having more knowledge about their processes, but also to get rid of the noise. I found it super fun and quick to get started using Postman to test the APIs to get all sorts of wonderful data from basic info about a project to detailed test results and so on... The backend project is using Node.js but this post is not specific to that but more an intended general reference guide for using the Teamcity API, I hope you find this useful. ✨

ℹ For more info visit [Teamcity REST API documentation](https://www.jetbrains.com/help/teamcity/rest/teamcity-rest-api-documentation.html). ℹ

## Useful bits 😎
* Authenticate your request via bearer token in the header
* Locators - filter string in URL (more info in the link above ^)
* Response format - set in the HTTP `Accept` header for either plain text, JSON or XML. The default format is XML
* By default the max. list amount response will be 100

### $long ⭐⭐⭐⭐⭐
This is extremely useful to know and it essentially returns all fields from a chosen resource. _The official documentation recommends not to use $long for actual requests but more of a check to see what fields you might need._

For example, let's say I want to get a specific build:

My go-to default is a request URL like this: 
`GET http://teamcity:8111/app/rest/builds/id:308`

But now I want to see only the test info from this build, so my first step would be to check for all fields to see what I needed like so: 
`GET http://teamcity:8111/app/rest/builds?locator=id:308&fields=build($long)`

From the response I was able to manually refine down the fields I needed and adjusted the URL:
`http://teamcity:8111/app/rest/builds?locator=id:308&fields=build(testOccurrences(count,passed,ignored))` and this response would look like this:
```
{
    "build": [
        {
            "testOccurrences": {
                "count": 201,
                "passed": 199,
                "ignored": 2
            }
        }
    ]
}
```

**Interestingly...** The response is different between `/app/rest/builds/id:308` and `/app/rest/builds/locator=id:308` where the 1st has more detail. I'm going to look into this more.  🤓

## Example Requests 
Presenting a selection of useful requests - not every single one, and _**please note for readability purposes (and less page noise) I've reduced some of the responses but tried including the 'main' bits**_ if that makes sense! The following examples assumes the base URL is `http://teamcity:8111/app/rest` for simplicity.
 
### Builds 🔨
#### Get build
`GET http://teamcity:8111/app/rest/builds?locator=id:308&fields=count,build(id,buildTypeId,number,status,state,branchName,webUrl)`
```
{
    "count": 1,
    "build": [
        {
            "id": 308,
            "buildTypeId": "projectA_Release",
            "number": "7.5.0.6352",
            "status": "SUCCESS",
            "state": "finished",
            "branchName": "branchA",
            "webUrl": "http://teamcity:8111/viewLog.html?buildId=308&buildTypeId=projectA_Release"
        }
    ]
}
```

#### Get build related issues
`GET http://teamcity:8111/app/rest/builds/id:308/relatedIssues`
```
{
    "href": "/app/rest/builds/id:308/relatedIssues",
    "issueUsage": [
        {
            "changes": {
                "change": [
                    {
                        "id": 26019,
                        "version": "b300f534a2733530c623d41ddcf59690053e580d",
                        "username": "lorna.watson",
                        "date": "20210210T142232+0000",
                        "href": "/app/rest/changes/id:26019",
                        "webUrl": "http://teamcity:8111/viewModification.html?modId=26019&personal=false"
                    }
                ],
                "count": 1
            },
            "issue": {
                "url": "http://jira:8111/browse/feature-98",
                "id": "feature-98"
            }
        }
    ],
    "count": 1
}
```

#### Get build queue 
`GET http://teamcity:8111/app/rest/buildQueue`
```
{
    "count": 0,
    "href": "/app/rest/buildQueue",
    "build": []
}
```

#### Get build types
`GET http://teamcity:8111/app/rest/buildTypes?locator=affectedProject:(id:projectA)&fields=buildType(id,name,projectName,projectId,webUrl,steps(count))`
```
{
    "buildType": [
        {
            "id": "projectA_Release",
            "name": "Project A Release",
            "projectName": "Project A",
            "projectId": "projectA",
            "webUrl": "http://teamcity:8111/viewType.html?buildTypeId=projectA_Release",
            "steps": {
                "count": 6
            }
        }
}
```
 
#### Get list of build types in a project with the status of the last finished build for each build configuration
`GET http://teamcity:8111/app/rest/buildTypes?locator=affectedProject:(id:projectA)&fields=buildType(id,name,builds($locator(running:false,count:1),build(number,status,statusText,finishDate)))`
```
{
    "buildType": [   
        {
            "id": "projectA_Release",
            "name": "Project A Release",
            "builds": {
                "build": [
                    {
                        "number": "35",
                        "status": "FAILURE",
                        "statusText": "Tests passed: 199, ignored: 2; unable to create or deploy release. Please check the build log for details on the error. (new)",
                        "finishDate": "20210209T102244+0000"
                    }
                ]
            }
        }
    ]
}
```

### Changes 🎎
#### Get the list of all changes included into the build
`GET http://teamcity:8111/app/rest/changes?locator=build:(id:buildId)`
```
{
    "change": [
        {
            "id": 26,
            "username": "lorna.watson",
            "date": "20210210T142232+0000",
            "webUrl": "http://teamcity:8111/viewModification.html?modId=260191&personal=false"
        },
        {
            "id": 27,
            "username": "lorna.watson",
            "date": "20210210T142219+0000",
            "webUrl": "http://teamcity:8111/viewModification.html?modId=260190&personal=false"
        }
    ],
    "count": 2
}
```

#### Get details of an individual change 
`GET http://teamcity:8111/app/rest/changes/id:changeId`
```
{
    "id": 26, 
    "username": "lorna.watson",
    "date": "20210210T142232+0000",
    "webUrl": "http://teamcity:8111/viewModification.html?modId=260191&personal=false",
    "comment": "Merge branch into master",
    "user": {
        "username": "hello@lorna.dev",
        "name": "Lorna Watson",
        "id": 1,
    },
    "files": {
        "count": 0,
        "file": []
    }
}
```

#### Get all changes for a project 
`GET http://teamcity:8111/app/rest/changes?locator=project:projectA&fields=change(id,username,date,webUrl)`
```
{
    "change": [
        {
            "id": 27,
            "username": "lorna.watson",
            "date": "20210225T142606+0000",
            "webUrl": "http://teamcity:8111/viewModification.html?modId=270391&personal=false"
        },
        {
            "id": 28,
            "username": "bill.smith",
            "date": "20210225T135430+0000",
            "webUrl": "http://teamcity:8111/viewModification.html?modId=270344&personal=false"
        },
        {
            "id": 29,
            "username": "bill.smith",
            "date": "20210225T120351+0000",
            "webUrl": "http://teamcity:8111/viewModification.html?modId=270343&personal=false"
        }
    ],
    "count": 3
}
```
  
### Projects 🧾
#### Get list of projects 
`GET http://teamcity:8111/app/rest/projects` 
```
{
    "count": 2,
    "href": "/app/rest/projects",
    "project": [
        {
            "id": "_Root",
            "name": "<Root project>",
            "description": "Contains all other projects",
            "href": "/app/rest/projects/id:_Root",
            "webUrl": "http://teamcity:8111/project.html?projectId=_Root"
        },
        {
            "id": "projectA",
            "name": "Project A",
            "parentProjectId": "_Root",
            "href": "/app/rest/projects/id:projectA",
            "webUrl": "http://teamcity:8111/project.html?projectId=projectA"
        }
    ]
}
```

### Servers ⚙
#### Get server version 
`GET http://teamcity:8111/app/rest/version`
```
85899
```

### Agents 🚔
#### Get list of agents
`GET http://teamcity:8111/app/rest/agents`

#### Get agent details
`GET http://teamcity:8111/app/rest/agents/id:5109`
 
## Summary
You can really do so much with the well-documented Teamcity API and whilst I've touched base on some parts i.e. project and build requests, you can explore other areas such as: investigations, branches, problems, users, agents and so on. Please see the link at the top of this post for more details. I need to play around more with locators but so far really loving the filtering! I've tried to keep the detail to minimum but also informative, didn't want to blag the whole page with JSON responses 😅. I hope this has been useful and clear to follow, thanks!! 😇 

![](https://media.giphy.com/media/xT1XGLzd2biV4QLWY8/giphy.gif)