Overview

I spoke to someone recently on the subject of learning to use ScriptRunner and Groovy.  One of the questions he had was around the number of results that were returned when he called the API.  That is, he had set maxResults to 500, but only 100 results were returned.   Why?
It’s true that you have some agency over the number of results that are returned by the Jira (and Confluence) REST API.  You can set the maxResults value as part of the request, and receive more than the 50 items that are returned by default.   However, there are limits to this parameter.  The API has a built-in limit, and no matter what you set maxResults to, you cannot exceed that limit.

Let’s look at an example.

If I call /rest/api/3/search?jql=project is not EMPTY, it’ll return every issue in the instance from a project that isn’t empty.  That’s potentially a lot of issues. 
However if we look at the results that are returned, there are some meta attributes in addition to the issues themselves.  In our example, the JSON that is returned starts with this:
{
"expand": "schema,names",
"startAt": 0,
"maxResults": 50,
"total": 35,
From this, we see that the number of results returned by default is 50, as we have not specified another value. Let’s try calling the same API endpoint, but we specify maxResults.   We do this by tacking maxResults onto the end: /rest/api/3/search?jql=project is not EMPTY&maxResults=200

Here are the results:

{
	"expand": "schema,names",
	"startAt": 0,
	"maxResults": 100,
	"total": 35,	
Okay so we’ve affected the maxResults value, but it’s only 100.   This tell us that the maximum number of results that the API will return to us on a single page is 100.   How do we get the rest?  We use pagination.
Pagination literally means that we’re going through the pages of results, rather than working with a single page of results that is returned. APIs do this to ensure that they don’t get overloaded while processing, and that your system doesn’t get overloaded by the results.  The meta-attributes we’ve been looking at give us all the information we need to paginate through the list of issues.  Let’s examine them one at a time.
If you imagine that the list of possible results is 1000 issues long, and that each issue is assigned a number in that list, startAt is the issue that leads the page of results.  In other words, if we specify startAt to be 50, the page of results that is returned will start at item #50 and run to #150.

We already know what maxResults is.

Total is, simply, the number of results that were returned on the given page.
With these, we have the pieces we need to paginate through the entire list. 

Code Example

Here’s a basic script that performs pagination:
def maxResults = 100
def startAt = 0
def stillPaginating = true

//Define the global variables

while (stillPaginating == true){
   
    //We need to paginate through the results
    //We're iterating by 100 results at a time
   
    def getIssueKeys = get("/rest/api/3/search?jql=project%20is%20not%20EMPTY&maxResults=${maxResults}&startAt=${startAt}")
        .header('Content-Type', 'application/json')
        .asObject(Map)
    //We start by returning all of the issues in the instance with a JQL search
   
    getIssueKeys.body.issues.each{ issueKey ->
   
       //do something with the issues
   
            if(getIssueKeys.body.total < maxResults){
            stillPaginating = false;
        }
        //Finally, we check to see if we're still paginating
        //If the total number of results is less than the total number of maximum possible results,
        //We must be at the end of the line and can stop paginating by terminating the loop
       startAt += 100
}

The first thing we do is define our maxResults and the startAt values.  We also define a boolean object, stillPaginating, and set it to true by default.

Next we define a while loop. This loop runs as long as stillPaginating remains true.   

We make an HTTP  GET request to the API endpoint that we’ve been discussing. This request takes the two values that we already defined, maxResults and startAt.
Presumably you’d want to do something with the issue keys that are returned, so we have a loop that does that.   We also check to see if the number of results that were returned were less than the maximum possible results.  In other words, if 35 results were returned by maxResults was 100, we can assume that we’ve reached the end of the list of possible results.  If this is the case, we break the loop by setting the value of stillPaginating to false.
If we’re still receiving full pages of results, we increment startAt by 100 and continue looping.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>