Bulk Fetch Test Cases
Bulk fetch test cases in a single request, with filtering and field selection defined in the request body. Use this API to get test case metadata in volume.
Endpoint
POST /rest/testcases/bulk/fetch
Request Headers
Content-Type: application/json
Name | Located in | Required | Description | Default | Schema |
|---|---|---|---|---|---|
apiKey | header | yes | Open API key generated in Integration, Open API in QMetry Test Management. | - | string |
scope | header | no | Project or scope Id/Key/Name. | default | string |
Content-Type | header | yes | Enter value "application/json". | - | string |
Request Parameters
Name | Located in | Required | Description | Default | Schema |
|---|---|---|---|---|---|
projectId | body | yes | Unique identifier of the project whose test cases you want to retrieve. Only one project per request is supported. | - | integer |
filters | body | no | System defined field and user defined field filters to apply. See the filter tables below. Ignored when | - | object |
fields | body | no | Names of the fields to return. Omit to return all system defined fields and all user defined fields. | - | Array[string] |
tsId | body | no | Unique identifier of a test suite. Returns only the test cases linked to that test suite. All | - | integer |
next | body | no | Pagination cursor returned in | - | string |
Filter System Defined Fields
Place system defined field filters inside the filters object.
Filter | Type | Description |
|---|---|---|
tcIds | Array[integer] | Returns only the test cases with the listed identifiers. Maximum 500 entries per request. |
isArchived | boolean | Set to false to return active test cases, or true to return archived test cases only. |
isShared | boolean | Set to true to return shared test cases, or false to return test cases that are not shared. |
priorityAlias | Array[string] | Returns test cases that match any one of the supplied priority labels. |
summary | string | Matches the test case summary. Use an asterisk as a wildcard for prefix, suffix, or substring matching, for example, tc1*. |
createdDateFrom | string | Start of the created date range, in YYYY-MM-DD format. The boundary date is included in the results. |
createdDateTo | string | End of the created date range, in YYYY-MM-DD format. The boundary date is included in the results. |
Filter User Defined Fields
Place a user defined field filter inside the filters object, using the exact field name as the key. Field names are case sensitive. To find a field name, go to Customization, open Manage Fields, and read the value in the Field Name column.
Field type | Value format | Behavior |
|---|---|---|
STRING | string | Matches the supplied text value. |
NUMBER | number | Matches the supplied numeric value. |
LOOKUPLIST | string | Matches the display label of the list value. |
MULTILOOKUPLIST | Array[string] | Values within the same field are evaluated as OR. A test case is returned when it holds any one of the supplied values. |
DATETIMEPICKER | object | Pass {"from": "YYYY-MM-DD", "to": "YYYY-MM-DD"}. Both boundary dates are included in the results. A plain string is not accepted. |
LARGETEXT | Not supported | Large text fields can be returned through fields, but cannot be used as filter keys. |
Note: Filter input dates always use the YYYY-MM-DD format. Dates in the response use the date and time format of the API key user.
Select the Fields to Return
Pass a fields array to limit the response to the columns you need. Omit fields to receive every system defined field and every user defined field configured for the project.
You can mix system defined field names and user defined field names in the same array.
A user defined field value appears as a top level key in each row. Rows in which the user defined field holds no value omit the key entirely, rather than returning a null value.
If fields contain a name that is neither a system defined field nor a user defined field configured for the project, the request is rejected before any query runs.
Paginate Through Results
The API returns a fixed page size of 500 test cases. The response carries a pagination object:
pagination.hasMoreindicates whether further pages are available. A value offalsemarks the last page.pagination.nextcarries an encrypted, opaque cursor. Send this value back as the top levelnextparameter to retrieve the following page.
Observe the following rules:
Pass the cursor value verbatim. Do not modify it or construct one manually.
The cursor embeds both the position in the result set and the scope that produced it, which includes any
filtersand anytsIdapplied on the first page. Do not resend those parameters with the cursor.When
nextis present, it always takes precedence. Anyfilters,tsId, or other body parameters sent in the same request are ignored without warning, and the response reflects the cursor scope.Pagination is sequential. You cannot jump to an arbitrary page.
No total record count is returned.
projectIdremains mandatory when you send a cursor.
Restrict Results to a Test Suite
Send tsId to return only the test cases associated with that test suite. Note the following behavior:
All
filterssent alongsidetsIdare discarded without warning. The response contains every test case in the test suite, archived and active, across all statuses.Field selection through
fieldscontinues to work normally.Cursor pagination continues to work normally, and the cursor carries the test suite scope.
If the test suite does not belong to the project named in
projectId, the request is rejected before any query runs.
Data Scope and Limitations
The API always serves the latest unarchived version of each test case. When the approval workflow is enabled for the project, it serves the latest approved version.
User defined field values appear as top level keys in each row. When a user defined field holds no value for a test case, the key is omitted from that row.
Results are always ordered by
tcIDin ascending order. Custom sorting is not supported.Test step data and test step level user defined fields are not returned.
Rows where the UDF has no value, key is omitted.
Historical test case versions are not returned.
Test execution data is outside the scope of this API.
Cross project retrieval in a single request is not supported.
Response
Content-Type: application/json
Status Code | Reason | Response Model |
|---|---|---|
200 | Test case list fetched successfully. | ResponseEntity |
400 | projectId is missing, the pagination cursor is invalid or expired, or tsId does not belong to the supplied project. | - |
403 | The fields array contains an unknown field name, filters.tcIds exceeds 500 entries, or the API key user holds no role in the requested project. | - |
500 | Returned when server connection times out. | - |
Examples
Retrive Test Cases
Request
{
"projectId": 43
}
Response
{
"data": [
{
"tcID": 7,
"entityKey": "1FHB-TC-6",
"summary": "User Login Scenario",
"description": "Verify user login with valid credentials",
"tcVersion": 2,
"tcVersionID": 7,
"priority": 100,
"priorityAlias": "P2",
"testCaseStateAlias": "Open",
"testCaseStateLabel": "Open",
"testCaseTypeAlias": "Functional",
"testingTypeAlias": "Manual",
"componentAlias": "Comp1,Comp2",
"ownerAlias": "admin",
"executionMinutes": "270",
"isArchived": false,
"isShared": false,
"projectId": 43,
"projectName": "QMetry",
"folderPath": "/Project-1/folder1 : /Project-1/folder2",
"folderMappings": [
{ "folderPath": "/Project-1/folder1", "tcFolderID": 46 },
{ "folderPath": "/Project-1/folder2", "tcFolderID": 47 }
],
"createdBy": "admin",
"createdDate": "10-11-2023 03:53:33 PM",
"updatedBy": "admin",
"updatedDate": "10-11-2023 04:33:30 PM",
"lookuplistdemo": "New"
}
],
"pagination": {
"hasMore": true,
"next": "e4ebf0628dc29626f2e7d239a9b4aa2a"
}
}
Send the cursor from pagination.next of the previous response as the top level next parameter. Do not resend filters, because the cursor already carries them.
Request
{
"projectId": 43,
"next": "e4ebf0628dc29626f2e7d239a9b4aa2a"
}
Response
{
"data": [
{ "tcID": 664, "entityKey": "1FHB-TC-664" },
{ "tcID": 665, "entityKey": "1FHB-TC-665" }
],
"pagination": {
"hasMore": true,
"next": "83501e16ec57d283917fb7272cf331a0"
}
}
When a request contains both next and filters, the filters embedded in the cursor apply and the filters in the request body are ignored. In this example, the cursor was produced by a first page filtered on isArchived set to false, so every row is active even though the request body asks for archived test cases.
Request
{
"projectId": 43,
"next": "e4ebf0628dc29626f2e7d239a9b4aa2a",
"filters": {
"isArchived": true
}
}
Response
{
"data": [
{ "tcID": 664, "isArchived": false },
{ "tcID": 665, "isArchived": false }
],
"pagination": {
"hasMore": true,
"next": "83501e16ec57d283917fb7272cf331a0"
}
}
Filter on System Defined Fields
Pass one or more priority labels as an array. Every returned test case matches at least one of the supplied labels.
Request
{
"projectId": 43,
"filters": {
"priorityAlias": ["Blocker"]
}
}
Response
{
"data": [
{ "entityKey": "1FHB-TC-6", "priorityAlias": "Blocker" },
{ "entityKey": "1FHB-TC-20", "priorityAlias": "Blocker" }
],
"pagination": { "hasMore": false }
}
Set isArchived to false to return active test cases, or true to return archived test cases only.
Request
{
"projectId": 43,
"filters": {
"isArchived": false
}
}
Response
{
"data": [
{ "entityKey": "1FHB-TC-6", "isArchived": false }
],
"pagination": {
"hasMore": true,
"next": "8123620a73cb2e32d80d08f32cc19c4b"
}
}
Set isShared to true to return shared test cases, or false to return test cases that are not shared.
Request
{
"projectId": 43,
"filters": {
"isShared": false
}
}
Response
{
"data": [
{ "entityKey": "1FHB-TC-6", "isShared": false }
],
"pagination": {
"hasMore": true,
"next": "5d1f0a9c2b7e4d38a6c1f09e7b3a2d54"
}
}
Use an asterisk anywhere in the value for prefix, suffix, or substring matching. The value tc1* matches summaries such as tc1 and tc1_login.
Request
{
"projectId": 43,
"filters": {
"summary": "tc1*"
}
}
Response
{
"data": [
{ "entityKey": "1FHB-TC-6", "summary": "tc1" }
],
"pagination": { "hasMore": false }
}
Returns only the listed test cases. Because the result set is limited to the supplied identifiers, hasMore is always false. You can send up to 500 identifiers per request. For a larger set, split the identifiers across several requests.
Request
{
"projectId": 43,
"filters": {
"tcIds": [7, 165]
}
}
Response
{
"data": [
{ "tcID": 7, "entityKey": "1FHB-TC-6" },
{ "tcID": 165, "entityKey": "1FHB-TC-20" }
],
"pagination": { "hasMore": false }
}
Pass both dates in YYYY-MM-DD format. Both boundary dates are included in the results.
Request
{
"projectId": 43,
"filters": {
"createdDateFrom": "2025-07-01",
"createdDateTo": "2025-07-31"
}
}
Response
{
"data": [
{ "entityKey": "1FHB-TC-20", "createdDate": "07-30-2025 10:39:39 AM" }
],
"pagination": { "hasMore": false }
}
Select Fields
Pass a fields array to receive only the listed columns. The priority field returns the numeric identifier of the priority list value, not the label. To get the label, request priorityAlias.
Request
{
"projectId": 43,
"fields": ["entityKey", "summary", "priority"]
}
Response
{
"data": [
{ "tcID": 7, "entityKey": "1FHB-TC-6", "summary": "tc1", "priority": 12345 }
],
"pagination": {
"hasMore": true,
"next": "b7c2e19f04a36d58e1f2a9c07d4b3e61"
}
}
Add the user defined field name to fields. When the field holds no value for a test case, the key is omitted from that row, as in the first row of this response.
Request
{
"projectId": 43,
"fields": ["entityKey", "summary", "lookuplistdemo"]
}
Response
{
"data": [
{ "tcID": 7, "entityKey": "1FHB-TC-6", "summary": "tc1" },
{ "tcID": 165, "entityKey": "1FHB-TC-20", "summary": "root", "lookuplistdemo": "New" }
],
"pagination": {
"hasMore": true,
"next": "c91d3a7e25f06b48d2e1a0f93c7b5d82"
}
}
Filter on User Defined Fields
Replace plannedDate with the name of a DATETIMEPICKER user defined field configured in your project. The value is returned in the date and time format of the API key user. When the field holds no value for a test case, the key is omitted from that row.
Request
{
"projectId": 57,
"fields": ["entityKey", "summary", "plannedDate"]
}
Response
{
"data": [
{ "tcID": 155, "entityKey": "KEV1-TC-1", "summary": "Testcase_1", "plannedDate": "04-23-2025 06:30:00 PM" },
{ "tcID": 156, "entityKey": "KEV1-TC-2", "summary": "Test_2", "plannedDate": "03-25-2025 06:30:00 PM" }
],
"pagination": { "hasMore": false }
}
Use the field name as the key and the display label of the list value as the value.
Request
{
"projectId": 43,
"filters": {
"lookuplistdemo": "New"
}
}
Response
{
"data": [
{ "entityKey": "1FHB-TC-20", "lookuplistdemo": "New" }
],
"pagination": { "hasMore": false }
}
Pass the values as an array. The values are evaluated as OR, so the following request returns every test case that holds either value, or both.
Request
{
"projectId": 43,
"filters": {
"multilookfield6": ["CC", "BB"]
}
}
Response
{
"data": [
{ "entityKey": "1FHB-TC-7", "multilookfield6": "BB,CC" },
{ "entityKey": "1FHB-TC-32", "multilookfield6": "Alpha,BB,Beta,CC,Gamma" },
{ "entityKey": "1FHB-TC-37", "multilookfield6": "Alpha,CC,Gamma" }
],
"pagination": { "hasMore": false }
}
Replace plannedDate with the name of a DATETIMEPICKER user defined field configured in your project. The value must be an object with from and to dates in YYYY-MM-DD format. Both boundary dates are included, so test cases 163 and 156, which fall on the boundary dates, appear in the results. Test case 155, planned for April 23, 2025, falls outside the range and is excluded.
Request
{
"projectId": 57,
"filters": {
"plannedDate": {
"from": "2025-03-16",
"to": "2025-03-25"
}
}
}
Response
{
"data": [
{ "tcID": 156, "entityKey": "KEV1-TC-2", "plannedDate": "03-25-2025 06:30:00 PM" },
{ "tcID": 158, "entityKey": "KEV1-TC-4", "plannedDate": "03-17-2025 06:30:00 PM" },
{ "tcID": 163, "entityKey": "KEV1-TC-9", "plannedDate": "03-16-2025 06:30:00 PM" }
],
"pagination": { "hasMore": false }
}
Retrieve the Test Cases of a Test Suite
Send tsId to return only the test cases linked to that test suite. The fields array continues to apply.
Request
{
"projectId": 43,
"tsId": 101,
"fields": ["tcID", "summary", "status"]
}
Response
{
"data": [
{ "tcID": 11, "summary": "Login smoke", "status": "Draft" }
],
"pagination": {
"hasMore": true,
"next": "3mK9vXpL8QrN2wYtA0bDcEfGhIjKlMnOpQrStUvWxYz1234ABCD=="
}
}
Request for the next page. The cursor carries the test suite scope, so do not resend tsId. If you send a different tsId together with the cursor, it is ignored, and the response still contains test cases from test suite 101.
{
"projectId": 43,
"next": "3mK9vXpL8QrN2wYtA0bDcEfGhIjKlMnOpQrStUvWxYz1234ABCD=="
}
Response
{
"data": [
{ "tcID": 511, "summary": "Checkout smoke" }
],
"pagination": {
"hasMore": true,
"next": "7pQwRmLkNvBcYdZaJhUeGfTyOi9sXwQp0An8E3uCjMrVb6KDlFhS=="
}
}
Any filters sent alongside tsId are discarded without warning. The response contains every test case in the test suite, archived and active, across all statuses.
Request
{
"projectId": 43,
"tsId": 101,
"filters": {
"isArchived": true,
"status": "Draft"
}
}
Response
{
"data": [
{ "tcID": 11, "isArchived": false, "status": "Approved" },
{ "tcID": 15, "isArchived": true, "status": "Approved" }
],
"pagination": { "hasMore": false }
}
Error Responses
{
"error": "projectId is required"
}
{
"error": "Invalid or expired pagination cursor"
}
{
"message": "tsId 9999 does not belong to project 43",
"status": 400
}
{
"error": "Unknown field: nonExistentField99"
}
{
"error": "tcIds filter cannot exceed 500 entries per request"
}
{
"error": "Access denied to project: 99999999"
}