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.!

2 answers

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

0 votes
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

Suggest an answer

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

Atlassian Community Events