---
title: Tests & Builds API | TestingBot API Documentation
description: Retrieve, update, stop and delete your Selenium, Cypress and Playwright
  test results, and group them into builds.
source_url:
  html: https://testingbot.com/support/api/tests
  md: https://testingbot.com/support/api/tests.md
---

# Tests & Builds

Fetch test results, report pass/fail state back from your CI, and work with the builds that group them.

- **Endpoint:** api.testingbot.com
- **Version:** v1
- **Format:** JSON
- **Auth:** [HTTP Basic](https://testingbot.com/support/api#authentication)

GET `/v1/tests`

## List your tests
 Paginated list of every test session belonging to the authenticated account, newest first. Filter by `browser_id`, `group`, or `build` to narrow the result. Use `since` to fetch only tests updated after a UNIX timestamp (poll-friendly). 
### Arguments

- **`offset` integer:** Skip this many tests from the start of the result set.
- **`count` integer max=`500`:** Number of tests to return .
- **`since` integer:** UNIX timestamp; return only tests updated at or after this time.
- **`browser_id` integer:** Filter to tests that ran on this browser\_id (from /v1/browsers).
- **`group` string:** Filter to tests tagged with this group name.
- **`build` string:** Filter to tests in this build (matches `capabilities.build`).
- **`skip_fields` string:** Comma-separated fields to omit (logs, thumbs).

### Response fields

- **`data` array of test case objects:** Test sessions for this page.
- **`meta` meta object:** —
- **`realtime` object:** ActionCable channel details for live updates. Only present on the `since`-polling form of this endpoint.

GET `/v1/tests`
[cURL](https://testingbot.com#) [.NET](https://testingbot.com#) [Ruby](https://testingbot.com#) [Python](https://testingbot.com#) [PHP](https://testingbot.com#) [Java](https://testingbot.com#) [NodeJS](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/tests?offset=0&count=10" \
-u key:secret
```

```csharp
var client = new TestingBotClient(key, secret);
var tests = await client.Tests.ListAsync();
```

```ruby
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.get_tests(0, 10)
```

```python
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.tests.get_tests(offset=0, limit=10)
```

```php
$api = new TestingBot\TestingBotAPI($key, $secret);
$api->getJobs(0, 10);
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
TestingbotTestCollection tests = restApi.getTests(0, 10);
```

```javascript
const TestingBot = require('testingbot-api');

const api = new TestingBot({
  api_key: "your-tb-key",
  api_secret: "your-tb-secret"
});

const tests = await api.getTests({ offset, limit });
```

Response

```json
{
  "data": [
    {
      "created_at": "2011-07-30T23:21:23Z",
      "completed_at": "2011-07-30T23:22:44Z",
      "id": 3,
      "name": "MyTest::testTitle",
      "session_id": "f7903f9e93e74fe1b0e924bf9d2ce9fc",
      "status_message": "Failed asserting that 1 equals 0.",
      "status_id": 0,
      "success": false,
      "browser": "iexplore",
      "browser_version": 8,
      "os": "WINDOWS",
      "duration": 13,
      "build": "buildid",
      "video": "https://s3.amazonaws.com/rectestingbot/sample.mp4",
      "groups": ["testingbot.com"]
    }
  ],
  "meta": { "offset": 0, "count": 10, "total": 789 }
}
```

GET `/v1/tests/:id`

## Get a specific test
 Returns the full detail record for a single test session: status, environment (browser/OS or device/platform), assets (video, logs, screenshots), groups, build, and duration. Accepts either the numeric test ID or the WebDriver session\_id. 
### Arguments

- **`id` string required:** Numeric test ID or WebDriver session\_id (UUID).
- **`skip_fields` string:** Comma-separated fields to omit from the response (logs, thumbs, visual\_run, groups).

### Response fields

- **`id` integer:** Unique numeric test ID.
- **`session_id` string:** Selenium / WebDriver session ID (UUID).
- **`name` string:** Human-readable test name set via capabilities or `update_test`.
- **`state` string:** Lifecycle state (RUNNING, COMPLETE, TIMEOUT, …).
- **`success` boolean:** Whether the test passed.
- **`status_id` integer:** Numeric status code (0=fail, 1=pass, 2=unknown).
- **`unknown` boolean:** Convenience flag, true when `status_id` is 2 (the test finished without reporting a pass/fail).
- **`status_message` string:** Failure reason or arbitrary status set via `test[status_message]`.
- **`created_at` timestamp:** When the test session started.
- **`completed_at` timestamp:** When the session ended; null while running.
- **`duration` integer:** Total run time in seconds.
- **`browser` string:** Browser identifier (e.g. "iexplore", "firefox").
- **`browser_version` integer:** Browser major version.
- **`os` string:** OS (e.g. "WINDOWS", "MAC").
- **`device_name` string:** Mobile device name when the session ran on a physical device; null otherwise.
- **`platform_name` string:** Mobile OS name; null on desktop.
- **`build` string:** Build identifier (free-form string set via capabilities).
- **`groups` array of string:** Tag/group names attached to the test.
- **`video` string:** Signed S3 URL to the recorded video, or false if video was disabled.
- **`thumbs` array of string:** Signed S3 URLs to screenshot thumbnails.
- **`logs` object:** Map of log names → signed S3 URLs (selenium, browser, …).
- **`assets_available` boolean:** Whether assets (video, logs, screenshots) have finished processing.
- **`extra` string:** Arbitrary metadata string set via `test[extra]`.
- **`type` string:** Driver type (WEBDRIVER, APPIUM).
- **`steps` string:** Rendered HTML of the recorded step list. Omit with `skip_fields=steps`.
- **`running` integer:** Completion percentage. Only present while a Codeless test is still running.

GET `/v1/tests/:id`
[cURL](https://testingbot.com#) [.NET](https://testingbot.com#) [Ruby](https://testingbot.com#) [Python](https://testingbot.com#) [PHP](https://testingbot.com#) [Java](https://testingbot.com#) [NodeJS](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/tests/:id" -u key:secret
```

```csharp
var client = new TestingBotClient(key, secret);
var test = await client.Tests.GetAsync(testId);
```

```ruby
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.get_test(test_id)
```

```python
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.tests.get_test(test_id)
```

```php
$api = new TestingBot\TestingBotAPI($key, $secret);
$api->getJob($test_id);
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
TestingbotTest test = restApi.getTest(test_id);
```

```javascript
const TestingBot = require('testingbot-api');

const api = new TestingBot({
  api_key: "your-tb-key",
  api_secret: "your-tb-secret"
});

const test = await api.getTestDetails(testId);
```

Response

```json
{
  "id": 3,
  "name": "MyTest::testTitle",
  "session_id": "f7903f9e93e74fe1b0e924bf9d2ce9fc",
  "state": "COMPLETE",
  "success": false,
  "status_id": 0,
  "unknown": false,
  "status_message": "Failed asserting that 1 equals 0.",
  "created_at": "2011-07-30T23:21:23Z",
  "completed_at": "2011-07-30T23:22:23Z",
  "duration": 60,
  "browser": "iexplore",
  "browser_version": 8,
  "os": "WINDOWS",
  "device_name": null,
  "platform_name": null,
  "build": null,
  "groups": ["testingbot.com"],
  "video": "https://s3.amazonaws.com/rectestingbot/sample.mp4",
  "thumbs": ["https://s3.amazonaws.com/euthumbtestingbot/3_f93782fji.jpg"],
  "logs": { "selenium": "https://s3-eu-west-1.amazonaws.com/eulogtestingbot/session.txt" },
  "assets_available": true,
  "type": "WEBDRIVER"
}
```

PUT `/v1/tests/:id`

## Update a test
 Updates a test's metadata after it's been recorded. Use this to mark a test as passed/failed from the test runner, attach groups/tags, set a build identifier, or add a status message. Accepts either a numeric test ID or a WebDriver session\_id. 
### Arguments

- **`id` string required:** Numeric test ID or WebDriver session\_id (UUID).
- **`test[name]` string:** Human-readable test name.
- **`test[success]` boolean:** true=pass, false=fail.
- **`test[status_message]` string:** Failure reason or arbitrary status text.
- **`test[extra]` string:** Arbitrary metadata.
- **`test[build]` string:** Build identifier to associate this test with.
- **`test[public]` boolean:** When true, makes the test publicly viewable via share URL.
- **`groups` string:** Comma-separated string or array of tag/group names to attach to the test.
- **`build` string:** Build identifier (alternative location to `test[build]`).

### Response fields

- **`success` boolean:** Whether the operation succeeded.
- **`errors` object:** Validation errors keyed by field name. Only present when `success` is false.
- **`error` string:** Single human-readable reason, used by the older endpoints in place of `errors`. Only present when `success` is false.

PUT `/v1/tests/:id`
[cURL](https://testingbot.com#) [.NET](https://testingbot.com#) [Ruby](https://testingbot.com#) [Python](https://testingbot.com#) [PHP](https://testingbot.com#) [Java](https://testingbot.com#) [NodeJS](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/tests/:id" \
-X PUT \
-d "test[success]=1" \
-d "groups[]=regression" \
-u key:secret
```

```csharp
var client = new TestingBotClient(key, secret);
await client.Tests.UpdateAsync(testId, new TestUpdate { Success = true, Groups = new[] { "regression" } });
```

```ruby
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.update_test(test_id, { name: 'new_name', success: true })
```

```python
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.tests.update_test(test_id, status_message='..', passed=1, build='..', name='..')
```

```php
$api = new TestingBot\TestingBotAPI($key, $secret);
$api->updateJob($test_id, ['name' => 'mytest', 'success' => true]);
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
boolean success = restApi.updateTest(testId, details);
```

```javascript
const TestingBot = require('testingbot-api');

const api = new TestingBot({
  api_key: "your-tb-key",
  api_secret: "your-tb-secret"
});

await api.updateTest({ 'test[success]': '1', 'test[status_message]': 'failure reason' }, testId);
```

Response

```json
{
  "success": true
}
```

DELETE `/v1/tests/:id`

## Delete a test
 Permanently deletes a test session and every associated asset (video, logs, screenshots). This action cannot be undone. 
### Arguments

- **`id` string required:** Numeric test ID or WebDriver session\_id of the test to delete.

DELETE `/v1/tests/:id`
[cURL](https://testingbot.com#) [.NET](https://testingbot.com#) [Ruby](https://testingbot.com#) [Python](https://testingbot.com#) [PHP](https://testingbot.com#) [Java](https://testingbot.com#) [NodeJS](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/tests/:id" \
-X DELETE \
-u key:secret
```

```csharp
var client = new TestingBotClient(key, secret);
await client.Tests.DeleteAsync(testId);
```

```ruby
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.delete_test(test_id)
```

```python
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.tests.delete_test(test_id)
```

```php
$api = new TestingBot\TestingBotAPI($key, $secret);
$api->deleteJob($test_id);
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
boolean success = restApi.deleteTest(testId);
```

```javascript
const TestingBot = require('testingbot-api');

const api = new TestingBot({
  api_key: "your-tb-key",
  api_secret: "your-tb-secret"
});

await api.deleteTest(testId);
```

Response

```json
{
  "success": true
}
```

PUT `/v1/tests/:id/stop`

## Stop a running test
 Terminates an in-flight test session. The test is marked complete; any assets gathered (video, logs, screenshots) become available for download. Returns 404 if the test has already finished. 
### Arguments

- **`id` string required:** Numeric test ID or WebDriver session\_id of the test to stop.

### Response fields

- **`success` boolean:** Whether the operation succeeded.
- **`errors` object:** Validation errors keyed by field name. Only present when `success` is false.
- **`error` string:** Single human-readable reason, used by the older endpoints in place of `errors`. Only present when `success` is false.

PUT `/v1/tests/:id/stop`
[cURL](https://testingbot.com#) [.NET](https://testingbot.com#) [Ruby](https://testingbot.com#) [Python](https://testingbot.com#) [PHP](https://testingbot.com#) [Java](https://testingbot.com#) [NodeJS](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/tests/:id/stop" \
-X PUT \
-u key:secret
```

```csharp
var client = new TestingBotClient(key, secret);
await client.Tests.StopAsync(testId);
```

```ruby
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.stop_test(test_id)
```

```python
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.tests.stop_test(test_id)
```

```php
$api = new TestingBot\TestingBotAPI($key, $secret);
$api->stopJob($test_id);
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
boolean success = restApi.stopTest(testId);
```

```javascript
const TestingBot = require('testingbot-api');

const api = new TestingBot({
  api_key: "your-tb-key",
  api_secret: "your-tb-secret"
});

await api.stopTest(testId);
```

Response

```json
{
  "success": true
}
```

POST `/v1/tests`

## Create a test record (manual sessions)
 Creates a TestCase record from outside the WebDriver/Appium flow. Mainly used by clients that want to log a manual or external test result against their TestingBot account. Most users will not need this — the normal flow is to start a Selenium/Appium session, and the record is created automatically. 
### Arguments

- **`test[name]` string required:** Test name.
- **`test[test_environment_id]` integer required:** Browser environment the result is attributed to (`browser_id` from GET /v1/browsers).
- **`test[success]` boolean:** true=pass, false=fail.
- **`test[status_message]` string:** Failure reason or status string.
- **`test[extra]` string:** Arbitrary metadata.
- **`test[build]` string:** Build identifier.

POST `/v1/tests`
[cURL](https://testingbot.com#) [PHP](https://testingbot.com#) [Java](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/tests" \
-u key:secret \
-d "test[name]=MyTest::testTitle" \
-d "test[test_environment_id]=1" \
-d "test[success]=true"
```

```php
$client = new TestingBot\Client($key, $secret);
$client->tests()->create([
    'name' => 'MyTest::testTitle',
    'test_environment_id' => 1,
    'success' => true,
]);
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
Map<String, Object> test = Map.of(
    "name", "MyTest::testTitle",
    "test_environment_id", 1,
    "success", true);
boolean success = restApi.createTest(test);
```

POST `/v1/tests/{id}`

## Update a test (POST alias)
 Alias of `PUT /v1/tests/:id` for clients that cannot send PUT requests (some older HTTP libraries). Same body schema and behavior — see PUT for the canonical form. 
### Arguments

- **`id` string required:** Numeric test ID or WebDriver session\_id (UUID).
- **`test[name]` string:** Human-readable test name.
- **`test[success]` boolean:** true=pass, false=fail.
- **`test[status_message]` string:** Failure reason or arbitrary status text.
- **`test[extra]` string:** Arbitrary metadata.
- **`test[build]` string:** Build identifier to associate this test with.
- **`test[public]` boolean:** When true, makes the test publicly viewable via share URL.
- **`groups` string:** Comma-separated string or array of tag/group names to attach to the test.

POST `/v1/tests/{id}`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/tests/{id}" \
-u key:secret
```

GET `/v1/builds`

## List your builds
 Returns a paginated list of test builds. A build is an aggregation of test cases that share the same `capabilities.build` identifier, useful for grouping CI runs. 
### Arguments

- **`offset` integer:** Skip this many builds from the start of the result set.
- **`count` integer max=`500`:** Number of builds to return .

### Response fields

- **`data` array of build objects:** Builds for this page.
- **`meta` meta object:** —

GET `/v1/builds`
[cURL](https://testingbot.com#) [.NET](https://testingbot.com#) [Ruby](https://testingbot.com#) [Python](https://testingbot.com#) [PHP](https://testingbot.com#) [Java](https://testingbot.com#) [NodeJS](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/builds?offset=0&count=10" \
-u key:secret
```

```csharp
var client = new TestingBotClient(key, secret);
var builds = await client.Builds.ListAsync();
```

```ruby
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.get_builds(0, 10)
```

```python
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.build.get_builds(offset=0, limit=10)
```

```php
$api = new TestingBot\TestingBotAPI($key, $secret);
$api->getBuilds(0, 10);
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
TestingbotBuildCollection builds = restApi.getBuilds(0, 10);
```

```javascript
const TestingBot = require('testingbot-api');

const api = new TestingBot({
  api_key: "your-tb-key",
  api_secret: "your-tb-secret"
});

const builds = await api.getBuilds(offset, limit);
```

Response

```json
{
  "data": [
    {
      "id": 4331,
      "build_identifier": "first-build",
      "created_at": "2016-08-01T11:09:02.000Z",
      "updated_at": "2016-08-01T11:09:02.000Z"
    }
  ],
  "meta": { "offset": 0, "count": 10, "total": 3 }
}
```

GET `/v1/builds/:id`

## Get tests for a build
 Returns all test cases that belong to a single build, with pagination. The build can be referenced by either its numeric internal ID or the string identifier you set via `capabilities.build`. 
### Arguments

- **`id` string required:** Numeric build ID or string build identifier.
- **`offset` integer:** Skip this many tests in the build.
- **`count` integer max=`500`:** Number of tests to return .
- **`skip_fields` string:** Comma-separated fields to omit from each test (logs, thumbs).

### Response fields

- **`data` array of test case objects:** Test sessions for this page.
- **`meta` meta object:** —
- **`realtime` object:** ActionCable channel details for live updates. Only present on the `since`-polling form of this endpoint.

GET `/v1/builds/:id`
[cURL](https://testingbot.com#) [.NET](https://testingbot.com#) [Ruby](https://testingbot.com#) [Python](https://testingbot.com#) [PHP](https://testingbot.com#) [Java](https://testingbot.com#) [NodeJS](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/builds/:id" \
-u key:secret
```

```csharp
var client = new TestingBotClient(key, secret);
var tests = await client.Builds.GetTestsAsync(buildId);
```

```ruby
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.get_build(build_identifier)
```

```python
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.build.get_tests_for_build(build_id)
```

```php
$api = new TestingBot\TestingBotAPI($key, $secret);
$api->getBuild($build_id);
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
TestingbotTestBuildCollection tests = restApi.getTestsForBuild(buildId);
```

```javascript
const TestingBot = require('testingbot-api');

const api = new TestingBot({
  api_key: "your-tb-key",
  api_secret: "your-tb-secret"
});

const tests = await api.getTestsForBuild(buildId);
```

Response

```json
{
  "data": [
    {
      "id": 3,
      "name": "MyTest::testTitle",
      "session_id": "f7903f9e93e74fe1b0e924bf9d2ce9fc",
      "state": "COMPLETE",
      "success": false,
      "status_id": 0,
      "created_at": "2011-07-30T23:21:23Z",
      "completed_at": "2011-07-30T23:22:44Z",
      "duration": 13,
      "browser": "iexplore",
      "browser_version": 8,
      "os": "WINDOWS",
      "build": "buildid",
      "video": "https://s3.amazonaws.com/rectestingbot/sample.mp4"
    }
  ],
  "meta": { "offset": 0, "count": 10, "total": 1 }
}
```

DELETE `/v1/builds/:id`

## Delete a build
 Permanently deletes a build and every test, asset (video, logs, screenshots) attached to it. This action cannot be undone. 
### Arguments

- **`id` string required:** Numeric build ID or string build identifier to delete.

DELETE `/v1/builds/:id`
[cURL](https://testingbot.com#) [.NET](https://testingbot.com#) [Ruby](https://testingbot.com#) [Python](https://testingbot.com#) [PHP](https://testingbot.com#) [Java](https://testingbot.com#) [NodeJS](https://testingbot.com#)
Request

```bash
$ curl "https://api.testingbot.com/v1/builds/:id" \
-X DELETE \
-u key:secret
```

```csharp
var client = new TestingBotClient(key, secret);
await client.Builds.DeleteAsync(buildId);
```

```ruby
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.delete_build(build_identifier)
```

```python
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.build.delete_build(build_id)
```

```php
$api = new TestingBot\TestingBotAPI($key, $secret);
$api->deleteBuild($build_id);
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
boolean success = restApi.deleteBuild(buildId);
```

```javascript
const TestingBot = require('testingbot-api');

const api = new TestingBot({
  api_key: "your-tb-key",
  api_secret: "your-tb-secret"
});

await api.deleteBuild(buildId);
```

Response

```json
{
  "success": true
}
```

POST `/v1/builds/{id}/ci`

## Link a build to a commit for CI status checks
 Associates a CI build with the commit/PR it ran for on a code host and posts an in-progress status check. Currently supports `github` (requires the TestingBot GitHub App installed on the repository owner). Call this at the start of a workflow run. 
### Arguments

- **`id` string required:** Build identifier (capabilities.build) for this run.
- **`repo` string required:** Repository in owner/name form.
- **`commit_sha` string required:** Head commit SHA the build ran for. For pull\_request events use github.event.pull\_request.head.sha (not the merge commit) so the check lands on the SHA branch protection evaluates.
- **`provider` string:** Code host provider. Currently only "github".
- **`branch` string:** Git branch name.
- **`pull_request_number` integer:** Pull request number, if any.

POST `/v1/builds/{id}/ci`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST "https://api.testingbot.com/v1/builds/{id}/ci" \
-u key:secret \
-d "repo=my-org/my-repo" \
-d "commit_sha=9fceb02d0ae598e95dc970b74767f19372d61af8" \
-d "branch=main"
```

PUT `/v1/builds/{id}/ci`

## Report a CI status-check build as finished
 Signals that the CI run for this build has finished, so the status check is updated with the aggregate pass/fail result. Call this after your tests complete. 
### Arguments

- **`id` string required:** Build identifier (capabilities.build) for this run.
- **`provider` string:** Code host provider. Currently only "github".

### Response fields

- **`success` boolean:** Whether the operation succeeded.
- **`errors` object:** Validation errors keyed by field name. Only present when `success` is false.
- **`error` string:** Single human-readable reason, used by the older endpoints in place of `errors`. Only present when `success` is false.

PUT `/v1/builds/{id}/ci`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X PUT "https://api.testingbot.com/v1/builds/{id}/ci" \
-u key:secret
```

[Previous User & Team Management](https://testingbot.com/support/api/user) [Next Insights](https://testingbot.com/support/api/insights)
