Forums

Articles
Create
cancel
Showing results for 
Search instead for 
Did you mean: 

/rest/api/3/search/jql API Deprecated — Need Alternative

Vitheya Monikha October 13, 2025

Hi Team,

We recently noticed that the JIRA REST API endpoint /rest/api/3/search/jql has been deprecated. Our application uses this API to fetch issues (such as test cases, test suites, and requirements) based on JQL queries.

Could you please confirm:

What is the recommended replacement or alternative API for this endpoint?

currently using /rest/api/3/search/jql?jql still we didn't get any anything



Here’s a snippet of how we currently use it:

1. resolver.define('getIssues', async ({ payload }) => {
  if (!payload || !payload.projectKey || !payload.issueType) {
  
    throw new Error("Missing required parameters (projectKey or issueType) for getIssues.");
  }
 
  const { projectKey, issueType } = payload;
  const jql = `project=${projectKey} AND issuetype="${issueType}"`;
 
  const encodedJql = encodeURIComponent(jql);

  const response = await api.asApp().requestJira(route`/rest/api/3/search?jql=${encodedJql}`, {
    method: "GET",
    headers: { "Accept": "application/json" }
  });
 
  if (!response.ok) {
     console.error(`Jira API call failed with status ${response.status}`);
     const errorData = await response.text();
     throw new Error(`Failed to fetch issues: ${errorData}`);
  }
 
  const data = await response.json();
  return data.issues || []; 
});

2.
resolver.define("fetchComponents", async ({ payload }) => {
  try {
    console.log("Payload Fetch...........", payload);
    const { projectKey } = payload;

    // Fetch all components
    const response = await api.asApp().requestJira(
      route`/rest/api/3/project/${projectKey}/components`,
      {
        method: "GET",
        headers: { "Accept": "application/json" },
      }
    );

    const components = await response.json();
    console.log("Fetch response......", components);

    if (!components || components.length === 0) return [];

    // Fetch issue count for each component (individually wrapped in try/catch)
    const componentsWithIssueCounts = await Promise.all(
      components.map(async (component) => {
        try {
          const jqlQuery = `component=${component.id} AND project=${projectKey}`;
          const issueCountResponse = await api
            .asApp()
            .requestJira(
              route`/rest/api/3/search/jql?jql=${encodeURIComponent(jqlQuery)}`,
              {
                method: "GET",
                headers: { "Accept": "application/json" },
              }
            );

          const issueData = await issueCountResponse.json();

          return {
            ...component,
            issueCount: issueData.total || 0, // Total issues linked to this component
          };
        } catch (innerError) {
          console.error(
            `❌ Error fetching issue count for component ${component.name}:`,
            innerError
          );
          return {
            ...component,
            issueCount: 0, // Fallback to 0 if issue count fetch fails
          };
        }
      })
    );

    return componentsWithIssueCounts;
  } catch (error) {
    console.error("❌ Error fetching components:", error);
    return [];
  }
});


3.
resolver.define("filterIssues", async ({ payload }) => {
  try {
    const { jqlQuery } = payload;
    console.log("Payload filterIssues,,,,,,,,,,", payload);
    const response = await api.asApp().requestJira(route`/rest/api/3/search/jql?jql=${jqlQuery}&maxResults=50`, {
      method: "GET",
      headers: { "Accept": "application/json" },
    });

    const issues = await response.json();
    console.log("Fetch filterIssues...... ", issues);
    return issues.issues || null;
  } catch (error) {
    console.error("Error in filterIssues:", error);
    return null;
  }
});


4.
resolver.define("getLinkedRequirement", async ({ payload }) => {
  try {
    const { customFieldKey, customFieldValue, projectId, issueType, startAt, maxResults } = payload;

    console.log("Payload for getLinkedRequirment fetch:", payload);

    const jql = `project="${projectId}" AND "Requirement Link Issue" ~ "${customFieldValue}" AND issuetype="${issueType}"`;

    const response = await api.asApp().requestJira(
      route`/rest/api/3/search/jql?jql=${jql}&startAt=${startAt}&maxResults=${maxResults}`,
      {
        method: "GET",
        headers: { "Accept": "application/json" },
      }
    );

    console.log("JQLLLLLL:", jql);
    const linkedRequirementIssues = await response.json();
    console.log(`Fetched issues (startAt: ${startAt}):`, linkedRequirementIssues);

    return linkedRequirementIssues || null;
  } catch (error) {
    console.error("Error in getLinkedRequirement:", error);
    return { error: "Failed to fetch linked requirements" };
  }
});

5.
resolver.define('getOptionsWithIssueCount', async ({ payload }) => {
  const { fieldId, projectId, issueType, fieldClause } = payload;
  console.log("Fetching options with issue count...", payload);

  try {
    // Fetch Field Contexts
    const fieldContextRes = await api.asApp().requestJira(route`/rest/api/3/field/${fieldId}/context`, {
      method: "GET",
      headers: { "Accept": "application/json" }
    });

    if (!fieldContextRes.ok) throw new Error(`Failed to fetch field context: ${fieldContextRes.statusText}`);

    const fieldContextData = await fieldContextRes.json();
    if (!fieldContextData.values || fieldContextData.values.length === 0) {
      console.warn("No field context found.");
      return [];
    }

    const contextId = fieldContextData.values[0].id;

    // Fetch Field Options
    const optionsRes = await api.asApp().requestJira(route`/rest/api/3/field/${fieldId}/context/${contextId}/option`, {
      method: "GET",
      headers: { "Accept": "application/json" }
    });

    if (!optionsRes.ok) throw new Error(`Failed to fetch field options: ${optionsRes.statusText}`);

    const optionsData = await optionsRes.json();
    const options = optionsData.values || [];

    const optionsWithIssueCount = await Promise.all(options.map(async (option) => {
      const jql = `project="${projectId}" AND "${fieldClause}"="${option.value}" AND issuetype="${issueType}"`;
      console.log("JQL...........", jql)
      const searchRes = await api.asApp().requestJira(route`/rest/api/3/search/jql?jql=project="${projectId}" AND "${fieldClause}"="${option.value}" AND issuetype="${issueType}"&maxResults=50`, {
        method: "GET",
        headers: { "Accept": "application/json" },
      });

      if (!searchRes.ok) {
        console.error(`Failed to fetch issues for option ${option.value}: ${searchRes.statusText}`);
        return { ...option, issueCount: 0 };
      }

      const searchData = await searchRes.json();
      return { ...option, issueCount: searchData.total || 0 };
    }));

    console.log("Final options with issue count:", optionsWithIssueCount);
    return optionsWithIssueCount;

  } catch (error) {
    console.error("Error fetching options with issue count:", error);
    return { error: error.message };
  }
});

6.
resolver.define("issuesInComponent", async ({ payload }) => {
  const {
    customFieldData,
    customFieldKey,
    projectId,
    issueType,
    startAt = 0, // default to 0
    maxResults = 50, // optional override
  } = payload;

  console.log("Payload for issue fetch:", payload);

  const response = await api.asApp().requestJira(
    route`/rest/api/3/search/jql?jql=project="${projectId}" AND "${customFieldKey}"="${customFieldData}" AND issuetype="${issueType}"&startAt=${startAt}&maxResults=${maxResults}`,
    {
      method: "GET",
      headers: { "Accept": "application/json" },
    }
  );

  const issues = await response.json();
  console.log(`Fetched issues (startAt: ${startAt}):`, issues);

  return issues || null;
});

7.
resolver.define("loadRequirements", async ({ payload }) => {
  try {
    const { projectId, issueType, startAt = 0, maxResults = 50 } = payload;

    console.log("Payload for Load requirements .....", payload);

    const jql = `project = "${projectId}" AND issuetype = "${issueType}"`;

    const response = await api.asApp().requestJira(
      route`/rest/api/3/search/jql?jql=${jql}&startAt=${startAt}&maxResults=${maxResults}`,
      {
        method: "GET",
        headers: { "Accept": "application/json" },
      }
    );

    const issues = await response.json();
    console.log("Fetch loadRequirements......", issues);

    return issues || null;
  } catch (error) {
    console.error("Error in loadRequirements:", error);
    return {
      error: true,
      message: error.message || "Failed to load requirements"
    };
  }
});

8.
resolver.define('getIssues', async ({ payload }) => {
  // Defensive check: Ensure payload exists and has the required fields
  if (!payload || !payload.projectKey || !payload.issueType) {
    console.error("❌ Error: 'projectKey' or 'issueType' missing from payload.", payload);
    throw new Error("Missing required parameters (projectKey or issueType) for getIssues.");
  }
 
  // Destructure after the check
  const { projectKey, issueType } = payload;
 
  // The JQL construction logic is correct, but ensure issueType is always quoted
  const jql = `project=${projectKey} AND issuetype="${issueType}"`;
 
  // Use URL encoding for the jql parameter for safety
  const encodedJql = encodeURIComponent(jql);

  const response = await api.asApp().requestJira(route`/rest/api/3/search/jql?jql=${encodedJql}`, {
    method: "GET",
    headers: { "Accept": "application/json" }
  });
 
  // Add error handling for the API response
  if (!response.ok) {
     console.error(`Jira API call failed with status ${response.status}`);
     const errorData = await response.text();
     throw new Error(`Failed to fetch issues: ${errorData}`);
  }
 
  const data = await response.json();
  console.log("Data for get issues..........", data)
  return data.issues || []; // Prefer returning an empty array over null for consistency
});


Thanks in Advance for your help in clarifying the new approach.!

3 answers

1 vote
Gor Greyan
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
October 13, 2025

Hi @Vitheya Monikha

The REST API you are using has been deprecated and has now been removed from Jira. You have to move everything to this new search api: 

https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-search/#api-rest-api-3-search-jql-get

https://developer.atlassian.com/changelog/#CHANGE-2046

0 votes
Vitheya Monikha October 13, 2025

Hi @Gor Greyan ,

 

Thank you for the update regarding the deprecation of the previous REST API. We are in the process of migrating our calls to the new /rest/api/3/search/jql endpoint.

Could you please clarify — with the new endpoint, where can we retrieve the total issue count for a JQL query? Previously, we relied on the total field from the response. Will it still be available in the new API, or is there a new way to fetch it?

Thanks in advance for your guidance.

Gor Greyan
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
October 13, 2025

Hi @Vitheya Monikha

You’re correct — the new Enhanced JQL Search API (/rest/api/3/search/jql) no longer returns the total field that was included in the  /rest/api/3/search responses.

Atlassian replaced the old pagination and counting model with a new token-based approach (nextPageToken), which improves performance but removes total from the standard response.

If you only need the total issue count for a JQL query, you should now use the new endpoint: See the doc - https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-search/#api-rest-api-3-search-approximate-count-post

 

POST /rest/api/3/search/approximate-count

Example request:

{ "jql": "project = MYPROJECT AND status = \"To Do\"" }

Example response:

 

{ "count": 1234 }

This endpoint gives you an approximate (and usually very close) count without fetching all the issues.

If you still need an exact count, the alternative is to page through all the results from /rest/api/3/search/jql using nextPageToken and count them manually — but this is much less efficient.

0 votes
Vitheya Monikha October 13, 2025

Hi @Gor Greyan ,

 

Thanks for the update and the links.

I just want to confirm the specific endpoint URL we should now use.
Currently, our application is calling:

/rest/api/3/search/jql?jql


After the deprecation, we are still not receiving any response from this endpoint. Could you please clarify the exact new endpoint or example request format we should use instead?

I provided the code snippet above kindly take a look that as well and provide the solution

Thanks in advance for your help!

Gor Greyan
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
October 13, 2025

Hi @Vitheya Monikha

Please try this one.

GET /rest/api/3/search/jql?jql=project = MYPROJECT AND status = "To Do"
Host: your-domain.atlassian.net
Authorization: Bearer <token>
Accept: application/json

POST /rest/api/3/search/jql
Host: your-domain.atlassian.net
Authorization: Bearer <token>
Content-Type: application/json
Accept: application/json

{
"jql": "project = MYPROJECT AND status = \"To Do\"",
"maxResults": 50,
"fields": ["summary", "assignee", "status"],
}

https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-search/#api-rest-api-3-search-jql-get

Vitheya Monikha October 13, 2025

Hi @Gor Greyan ,

 

Thank you for the update regarding the deprecation of the previous REST API. We are in the process of migrating our calls to the new /rest/api/3/search/jql endpoint.

Could you please clarify — with the new endpoint, where can we retrieve the total issue count for a JQL query? Previously, we relied on the total field from the response. Will it still be available in the new API, or is there a new way to fetch it?

Thanks in advance for your guidance.

Suggest an answer

Log in or Sign up to answer
DEPLOYMENT TYPE
CLOUD
PRODUCT PLAN
ENTERPRISE
TAGS
AUG Leaders

Atlassian Community Events