Introduction to the TestRail API

TestRail_API (Navy Logo).png

TestRail’s API can be used to integrate TestRail with various tools, frameworks and third-party applications. For example, many customers use the API to integrate their automated tests and submit test results to TestRail. The API can be used for various other tasks as well and you can find a small list of examples below.

The API is HTTP-based and can thus be used from virtually any framework, programming language and tool. Submitting data to TestRail via the API is done via simple POST requests. Requesting data is done through GET requests. All requests and responses use the JSON format and UTF-8 encoding.

The API is part of TestRail and can be enabled in TestRail’s administration area under Admin > Site Settings > API.

Some typical scenarios the API can be used for:

  • Submit test results from automated tests
  • Migrate test cases from legacy systems
  • Synchronize test cases between different systems
  • Create test runs and plans programmatically
  • Query information for integrations 

Before reading through the API reference, please make yourself familiar with TestRail’s entities such as cases, runs & results, suites etc. To do this, please refer to TestRail’s User Guide with getting started topics and best practices.

API rate limit

Please note that the API is rate-limited on TestRail Cloud to ensure optimal performance for all users and may throttle requests. TestRail might also return a 429 Too Many Requests response, which you are expected to handle. Such a response also includes a Retry-After header indicating how many seconds to wait before you are allowed to submit the next request.

To avoid rate limits on TestRail Cloud, try using bulk API endpoints (e.g. using as add_results_for_cases instead of add_results_for case), build a time delay into your API calls, or upgrade to TestRail Enterprise Cloud.

Rate limits for TestRail Cloud are as follows:

  • 180 Requests per instance, per minute for TestRail Cloud Professional subscriptions.
  • 300 Requests per instance, per minute for TestRail Cloud Enterprise subscriptions.

 Note: No API rate limits are built into TestRail Server installations.

API pagination

There are many bulk API endpoints available in TestRail that enable you to retrieve information about multiple cases, tests, or other TestRail entities with a single GET request. This makes it faster and more efficient to retrieve information from TestRail while simplifying the process and reducing the overall number of requests sent to the TestRail API (especially relevant if you are hitting the API rate limit on TestRail Cloud).

Bulk endpoints return records in pages. TestRail supports two pagination modes:

  • Offset pagination (limit, offset) – the default. Use it when you need to jump to a specific position, or when your integration already uses offset.
  • Cursor pagination (limit, use_cursor, cursor) – use it when you need to read a full collection reliably, even if records are added or deleted while you read it (for example, migrations, reporting exports, or AI/MCP agents).

Cursor pagination is optional. Existing integrations that use offset keep working without changes.

The maximum limit of 500 and cursor pagination require TestRail 10.10 or later.

Pagination parameters

These parameters apply to every endpoint listed under Supported endpoints.

Name Type Required Description
limit integer false The number of records the response should return, from 1 to 500 (the response size is 250 by default on TestRail Cloud)
offset integer false Where to start counting records from (the offset). Cannot be combined with use_cursor or cursor
use_cursor boolean false Set to true to get the first page with cursor pagination. Not needed on later pages
cursor string false Cursor token from _links.next of the previous response. Returns the next page of records. Cannot be combined with offset

Limit

When using a bulk GET endpoint (like get_cases, get_runs, or get_results_for_case) the default number of records that are returned is 250. You can use the limit parameter to reduce this number, or to increase it up to 500.

For example, if you’re using the get_runs API method and only wanted to return the first three test runs in a project with project_id of 3, you would send the following request:

GET https://{hostname}/index.php?/api/v2/get_runs/3&limit=3

Please note, you would need to replace {hostname} with the actual URL for your TestRail instance.

The expected response would look like this:

{
  "offset": 0,
  "limit": 3,
  "size": 3,
  "_links": {
    "next": "/api/v2/get_runs/3&limit=3&offset=3",
    "prev": null
  },
  "runs": [
    {
      "id": 1,
      ..
    },
    {
      "id": 2,
      ..
    },
    {
      "id": 3,
      ..
    }
  ]
}

You can specify an integer as low as 1 for the limit parameter and as high as 500, which is the max number of records allowed in the response. Using limit=500 halves the number of requests needed to read a large collection, which also helps you stay under the API rate limit on TestRail Cloud. If you try a value greater than 500 on TestRail Cloud, you’ll receive the following error:

{
  "error": "Field :limit is too large (maximum 500)."
}

Offset

The offset parameter is what is used to navigate to the next set of results. For example, if you are using the get_cases endpoint to retrieve data for all the test cases in your instance and don’t set a separate limit value, your initial request will return a maximum of 250 test cases records since the default number of results is 250.

Example output:

{
  "offset": 0,
  "limit": 250,
  "size": 250,
  "_links": {
    "next": "api/v2/get_cases/3&limit=250&offset=250",
    "prev": null
  },
  "cases": [
    {
      "id": 2604,
      "title": "Add watermark to document and verify print output",
      "section_id": 268,
      "template_id": 1,
      "type_id": 7,
      "priority_id": 4,
      "milestone_id": null,
      "refs": null,
      "created_by": 2,
      "created_on": 1631664168,
      "updated_by": 2,
      "updated_on": 1631664168,
      "estimate": null,
      "estimate_forecast": "8m 40s",
      "suite_id": 32,
      "display_order": 1,
      "is_deleted": 0,
      "custom_automation_type": 0,
      "custom_automation_status": null,
      "custom_preconds": null,
      "custom_steps": null,
      "custom_expected": "Etiam massa dolor, ornare sit amet, lacinia nec, bibendum ut, magna.\n\t\t\n* Nam feugiat, eros at commodo dictum,\n* Felis libero varius orci, in vulputate\n* Massa turpis scelerisque diam.\n* Nunc et felis est. Phasellus laoreet nibh vel augue\n* Faucibus at varius est pretium.\n\nQuisque pellentesque **mauris**.",
      "custom_steps_separated": null,
      "custom_mission": null,
      "custom_goals": null
    },
    ..
  ]
}

If you have more than 250 test cases in your project, you will need to send another GET request to retrieve the next set of records, this time setting the offset parameter to 250, like so:

GET https://{hostname}/index.php?/api/v2/get_cases/3&offset=250

The response would be the next 250 test cases since we’re using the get_cases method in the example above.

Note: In offset mode, get_cases returns test cases in section and display order. Other bulk endpoints return records ordered by the ID values of those entities.

To iterate to the next set of records, you can either use the next value in the _links object returned in the previous response, or you can manually iterate over the offset number until you reach the end of the offsets.

You can tell once you’ve reached the end of the number of records available two ways. If your current request contains the final record in whatever table you are querying, the response will return a null value for the next value in the _links object. Additionally, if you enter an offset that is larger than the number of records available, you’ll be presented with an empty array. For example:

GET https://{hostname}/index.php?/api/v2/get_cases/3&offset=10000000

Example output:

{
  "offset": 10000000,
  "limit": 250,
  "size": 0,
  "_links": {
    "next": null,
    "prev": "/api/v2/get_cases/3&offset=9999750&limit=250"
  },
  "cases": []
}

You can use this to determine if you’ve hit the end of the records or not in your automation.

 

Offset pagination counts positions at request time. If records are added or deleted between two requests, a record can be skipped or returned twice. Use cursor pagination when you need every record exactly once.

Cursor

With cursor pagination, each response tells you where the next page starts. The position is stored in a token, so records added or deleted between requests do not shift the pages. Cursor pagination requires TestRail 10.10 or later.

To read a collection with cursor pagination:

  1. Send a request to a supported endpoint with use_cursor=true and the limit you want.
  2. If more records are available, the response contains _links.next with a URL that includes a cursor parameter. All other parameters from your request, including filters, are kept in this URL.
  3. Send a GET request to that URL to get the next page.
  4. Repeat until _links.next is null.

For example, to get the first 500 test cases in a project with project_id of 3:

GET https://{hostname}/index.php?/api/v2/get_cases/3&limit=500&use_cursor=true

Example output:

{
  "limit": 500,
  "size": 500,
  "_links": {
    "next": "/api/v2/get_cases/3&limit=500&cursor=AAAB9GcqF2Q3vT..",
    "prev": null
  },
  "cases": [
    { "id": 1, "title": "..", .. },
    { "id": 2, "title": "..", .. },
    ..
  ]
}

To get the next page, send a request to the next URL:

GET https://{hostname}/index.php?/api/v2/get_cases/3&limit=500&cursor=AAAB9GcqF2Q3vT..

Cursor responses differ from offset responses in two ways:

  • The response does not include offset.
  • _links.prev is always null. Cursor pagination only moves forward.

 

Treat the cursor value as opaque. Do not decode, build, or modify it – always use the value returned by TestRail.

Sort order

In cursor mode, records are always sorted by ID:

  • Newest first (descending ID): get_plans, get_runs, get_results, get_results_for_case, get_results_for_run, get_test_states_for_run
  • Oldest first (ascending ID): all other supported endpoints

 

get_cases returns test cases in section and display order in offset mode, but in case ID order in cursor mode. If your integration depends on the order of test cases, use offset pagination.

Rules

  • Do not mix modes. offset cannot be sent together with use_cursor or cursor, even when offset=0. Do not switch modes part-way through reading a collection – start a new traversal instead.
  • Cursors expire. A cursor is valid for 24 hours after it is issued. If a cursor expires, restart the traversal with use_cursor=true.
  • Cursors are instance-specific. A cursor that was modified, or was issued by a different TestRail instance, is rejected.
  • Page size follows the same rules as offset pagination (1–500).

Requests that break these rules fail with 400 Bad Request and one of the following errors:

Cause Error
Invalid cursor Field :cursor is not a valid pagination cursor.
Expired cursor Field :cursor contains an expired pagination cursor.
offset combined with use_cursor or cursor Field :cursor cannot be combined with the offset parameter.

Example: read a full collection

The following script reads every test case in a project with project_id of 3 using cursor pagination. Replace {hostname}, the email address, and the API key with your own values.

#!/usr/bin/env bash
HOST="https://{hostname}"
AUTH="qa.lead@example.com:YOUR_API_KEY"
NEXT="/api/v2/get_cases/3&limit=500&use_cursor=true"

while [ "$NEXT" != "null" ]; do
  RESPONSE=$(curl -s -H "Content-Type: application/json" -u "$AUTH" \
    "$HOST/index.php?$NEXT")
  echo "$RESPONSE" | jq '.cases[] | {id, title}'
  NEXT=$(echo "$RESPONSE" | jq -r '._links.next')
done

Every page is a separate request and counts against the API rate limit on TestRail Cloud. If you receive 429 Too Many Requests, wait for the number of seconds in the Retry-After header, then send the same request again. The cursor stays valid during the wait, as long as it has not expired.

Pagination on TestRail Server

On TestRail Server, pagination depends on the API pagination setting under Admin > Site Settings > API:

  • Off – limit defaults to no limit. All records are returned in one response, and _links.next is null in both modes.
  • On – limit defaults to 250. A limit above 500 is lowered to 500 instead of returning an error.

Supported endpoints

Offset and cursor pagination are supported by the following bulk endpoints:

Resource Endpoints
Attachments get_attachments_for_case, get_attachments_for_plan, get_attachments_for_run, get_attachments_for_section
BDDs get_bdds
Cases get_cases, get_history_for_case
Datasets get_datasets
Groups get_groups
Milestones get_milestones
Plans get_plans
Projects get_projects
Results get_results, get_results_for_case, get_results_for_run, get_test_states_for_run
Roles get_roles
Runs get_runs, get_all_runs
Sections get_sections
Shared Steps get_shared_steps
Suites get_suites, get_all_suites
Tests get_tests
Users get_users
Variables get_variables

Compact responses

Some bulk GET endpoints accept the view parameter. With view=compact, each record in the response includes only its key fields, such as IDs, names, statuses, and timestamps. Compact responses are much smaller than full responses, which makes them faster to transfer and process. They are useful for AI/MCP agents, which are sensitive to response size, and for scripts that only need an overview of a collection.

The view parameter is optional. Requests without it return full responses, so existing integrations keep working without changes.

 

The view parameter requires TestRail 10.10 or later.

View parameter

Name Type Required Description
view string false compact to return only the fields listed under Supported endpoints and fields. full (default) returns all fields

For example, to get the test cases in a project with project_id of 3 in compact view:

GET https://{hostname}/index.php?/api/v2/get_cases/3&view=compact

Example output:

{
  "offset": 0,
  "limit": 250,
  "size": 250,
  "_links": {
    "next": "/api/v2/get_cases/3&view=compact&limit=250&offset=250",
    "prev": null
  },
  "cases": [
    {
      "id": 2604,
      "title": "Add watermark to document and verify print output",
      "section_id": 268,
      "suite_id": 32,
      "priority_id": 4,
      "type_id": 7,
      "updated_on": 1631664168
    },
    ..
  ]
}

Compact view does not change pagination. The response keeps the same offset, limit, size, and _links values, and you can combine view with the endpoint's filters and the pagination parameters.

Supported endpoints and fields

The view parameter is supported by the following endpoints. In compact view, each record includes only these fields:

Endpoint Fields in compact view
get_cases id, title, section_id, suite_id, priority_id, type_id, updated_on
get_milestones id, name, is_completed, due_on
get_plans id, name, milestone_id, is_completed, passed_count, failed_count, untested_count, updated_on
get_results_for_run id, test_id, status_id, created_on
get_runs id, name, suite_id, is_completed, milestone_id, passed_count, failed_count, untested_count, updated_on
get_sections id, name, parent_id, suite_id, depth
get_suites id, name, project_id

All other fields are not returned in compact view. This includes custom fields (custom_*), descriptions, refs, url, attachments, defects, the blocked, retest, and custom status counts of runs and plans, and nested data such as plan entries. To get these fields, use view=full or omit the parameter.

 

The only allowed values are compact and full. Any other value returns 400 Bad Request.

Conventions used in API Reference

The TestRail API manual uses a few conventions to indicate variable parameters that should be replaced in API request URLs and truncate the way some examples of responses are displayed in the documentation.

1. Variables with curly braces

Replace any values with curly braces { } in API requests with actual parameters.

For example, in the API request, GET index.php?/api/v2/get_project/{project_id}, replace {project_id} with the value for your actual project id.

So, if your project id is 10, the request would be GET index.php?/api/v2/get_project/10

2. Truncated response code examples

.. in a code block indicates truncating of the response, which is used purely for aesthetic purposes in the documentation and do not reflect actual response content from the TestRail API. This is used in examples throughout the API reference guide merely to shorten the space responses take in the documentation itself and hopefully make the article more readable.

For example, please see the below example of a response using the get_cases endpoint.

{
  "offset": 0,
  "limit": 250,
  "size": 250,
  "_links":{
    "next": "/api/v2/get_cases/1&limit=250&offset=250",
    "prev": null
  },
  "cases":[
    { "id": 1, "title": "..", .. },
    { "id": 2, "title": "..", .. },
    ..
  ]
}

How to find ID variables for API calls?

Many TestRail API endpoints require you to add an ID value in the request URL to specify certain projects, cases, test runs, and more. In the below section, you will learn where to find specific IDs in TestRail UI and how to use them as a parameter in an API request.

Project ID – project_id

  1. Go to the ‘Dashboard’ on your TestRail instance.
  2. Select a project from the list.
  3. Check the URL. The numeric character you see at the end is the project’s ID, or you can also get the
  4. ID next to project name (please make sure to omit the character ‘P’ when using the project ID in your API requests).

Introduction to the TestRail API ss1.png

For example, in API request, GET index.php?/api/v2/get_project/{project_id}, replace {project_id} with 3.

So, the request would look like GET index.php?/api/v2/get_project/3.

Case ID – case_id

  1. Go to the ‘Dashboard’ on your TestRail instance.
  2. Select a project from the list.
  3. Navigate to the ‘Test Cases’ tab.
  4. Select a test case from the list.
  5. Check the URL. The numeric character you see at the end is Test Case ID, or you can also get the ID next to Test Case Name. Please make sure to omit the character ‘C’ when using API requests.

Introduction to the TestRail API ss2.png

Attachment ID – attachment_id

  1. Go to the ‘Dashboard’ on your TestRail instance.
  2. Select a project from the list.
  3. Navigate to the ‘Test Cases’ tab.
  4. Select a test case from the list.
  5. Add an attachment.

For TestRail versions before 7.1 – Right-click on an attachment and select ‘Copy link address’. And, then use the attachment ID mentioned in the URL.

For TestRail version 7.1 and later – Click on the attachment. Then copy the alphanumeric ID from the URL on ‘Attachment Details’ screen.

Introduction to the TestRail API ss5.png

Milestone ID – milestone_id

  1. Go to the ‘Dashboard’ on your TestRail instance.
  2. Select a project from the list.
  3. Navigate to the ‘Milestones’ tab.
  4. Select a milestone from the list.
  5. Check the URL. The numeric character you see at the end is Milestone ID, or you can also get the ID next to Milestone Name. Please make sure to omit the character ‘M’ when using API requests.

Introduction to the TestRail API ss6.png

Test Plan ID – plan_id

  1. Go to the ‘Dashboard’ on your TestRail instance.
  2. Select a project from the list.
  3. Navigate to the ‘Test Runs and Results’ tab.
  4. Select a test plan from the list.
  5. Check the URL. The numeric character you see at the end is Test Plan ID, or you can also get the ID next to Test Plan Name. Please make sure to omit the character ‘R’ when using API requests.

Introduction to the TestRail API ss7.png

Test Run ID – run_id

  1. Go to the ‘Dashboard’ on your TestRail instance.
  2. Select a project from the list.
  3. Navigate to the ‘Test Runs and Results’ tab.
  4. Select a test run from the list.
  5. Check the URL. The numeric character you see at the end is Test Run ID, or you can also get the ID next to Test Run Name. Please make sure to omit the character ‘R’ when using API requests.

Introduction to the TestRail API ss8.png

 

Was this article helpful?
82 out of 128 found this helpful