How to get tickets from Jira using Python

This tutorial is part of a series on building product integrations.


Jira is a powerful project management tool used by teams to track and manage work tasks, often called 'tickets' or 'issues'. With Jira, individual users and teams can collaborate, prioritize, and monitor the progression of tasks – which in our experience simplifies workflows and enhances productivity. Because of immediate updates and strong reporting features, Jira is frequently used in software companies for project management, and for bug and issue tracking.

You might find it useful to pull tickets from a customer’s Jira system for multiple reasons (e.g. enhancing project management or communication). Integrating the Jira API into your application allows you to automate the process of ticket retrieval which allows you toinvestigate ticket data, improves your efficiency, and solves for a range of potential customer use cases.

In this article, we’ll cover:

  •  How to set up authentication for the Jira API
  •  How to extract uniform data from Jira, incorporating get requests to pull tickets from Jira
  •  How to utilize a single API endpoint to connect with multiple external ticketing systems

{{blog-cta-100+}}

Authenticating with Jira

Authentication for the Jira API is accomplished by using an encoded header. This header should be in the format: <code class='blog_inline-code'>Authorization: Basic {Email Address}:{API-KEY}</code>. The {Email Address} and {API-KEY} are your Jira account email address and API key, respectively and they should be base64 encoded. The API key should be generated from your Jira account settings.

Note: Be sure to replace {Email Address}, and {API-KEY} with your actual email address and API key.

Here’s an example of how to generate the base64 string in Python. This script uses the requests library to make the necessary GET requests to the Jira API, and the json library to parse and print the JSON response:


import base64

email = "example@example.com"
api_key = "your_api_key"
base64_encoded_credentials = base64.b64encode(f"{email}:{api_key}".encode("ascii")).decode("ascii")

This will generate a base64 string from your email and API key which you can use in the header for authentication.


import requests
import base64
import json

# Define your email and the API key
email = "your-email@example.com"
api_key = "your-api-key"

# Encode the credentials
credentials = base64.b64encode(f"{email}:{api_key}".encode('utf-8')).decode('utf-8')

headers = {
    "Authorization": f"Basic {credentials}",
    "Accept": "application/json"
}

# Define the domain
domain = "your-domain"

# Define the base URL
base_url = f"https://{domain}.atlassian.net/rest/api/3/search"

# Define the parameters
params = {
    "expand": "renderedFields",
    "maxResults": 50,
    "startAt": 0
}

# Initialize an empty list to store the tickets
tickets = []

# Loop until all tickets are fetched
while True:
    response = requests.get(base_url, headers=headers, params=params)
    data = response.json()

    # Add the fetched tickets to the list
    tickets.extend(data['issues'])

    # Check if there are more tickets to fetch
    if 'startAt' in data and data['startAt'] + data['maxResults'] < data['total']:
        # Update the startAt parameter for the next request
        params['startAt'] = data['startAt'] + data['maxResults']
    else:
        # If no more tickets, break the loop
        break

# Print the fetched tickets
print(json.dumps(tickets, indent=4))

How to get tickets from Jira

The <code class='blog_inline-code'>base64</code> library is used to encode the email and API key in the required format for the Authorization header. The script gets 50 tickets at a time (as defined by the <code class='blog_inline-code'>maxResults</code> parameter), and keeps getting more tickets until no more are available, by updating the <code class='blog_inline-code'>startAt</code> parameter for each subsequent request. The tickets are stored in the <code class='blog_inline-code'>tickets</code> list.

Here’s example of an individual item returned by this API Endpoint:

{
    "expand": "renderedFields,names,schema,operations,editmeta,changelog,versionedRepresentations",
    "id": "10100",
    "self": "https://jira.example.com/rest/api/2/issue/10100",
    "key": "PRJ-1",
    "fields": {
        "statuscategorychangedate": "2021-08-01T14:20:10.674+0000",
        "issuetype": {
            "self": "https://jira.example.com/rest/api/2/issuetype/3",
            "id": "3",
            "description": "A task that needs to be done.",
            "iconUrl": "https://jira.example.com/secure/viewavatar?size=xsmall&avatarId=10318&avatarType=issuetype",
            "name": "Task",
            "subtask": false,
            "hierarchyLevel": 0
        },
        "parent": {
            "id": "10001",
            "key": "PRJ-2",
            "self": "https://jira.example.com/rest/api/2/issue/10001",
            "fields": {
                "summary": "Parent Issue",
                "status": {
                    "self": "https://jira.example.com/rest/api/2/status/1",
                    "description": "The issue is open and ready for the assignee to start work on it.",
                    "iconUrl": "https://jira.example.com/images/icons/statuses/open.png",
                    "name": "Open",
                    "id": "1",
                    "statusCategory": {
                        "self": "https://jira.example.com/rest/api/2/statuscategory/2",
                        "id": 2,
                        "key": "new",
                        "colorName": "blue-gray",
                        "name": "To Do"
                    }
                },
                "priority": {
                    "self": "https://jira.example.com/rest/api/2/priority/3",
                    "iconUrl": "https://jira.example.com/images/icons/priorities/major.png",
                    "name": "Major",
                    "id": "3"
                },
                "issuetype": {
                    "self": "https://jira.example.com/rest/api/2/issuetype/5",
                    "id": "5",
                    "description": "The sub-task of the issue",
                    "iconUrl": "https://jira.example.com/secure/viewavatar?size=xsmall&avatarId=10316&avatarType=issuetype",
                    "name": "Sub-task",
                    "subtask": true,
                    "hierarchyLevel": 1
                }
            }
        },
        "project": {
            "self": "https://jira.example.com/rest/api/2/project/10000",
            "id": "10000",
            "key": "PRJ",
            "name": "Project",
            "projectTypeKey": "business",
            "simplified": true,
            "avatarUrls": {
                "48x48": "https://jira.example.com/secure/projectavatar?pid=10000&avatarId=10011",
                "24x24": "https://jira.example.com/secure/projectavatar?size=medium&pid=10000&avatarId=10011",
                "16x16": "https://jira.example.com/secure/projectavatar?size=xsmall&pid=10000&avatarId=10011",
                "32x32": "https://jira.example.com/secure/projectavatar?size=small&pid=10000&avatarId=10011"
            }
        },
        "fixVersions": [],
        "workratio": -1,
        "lastViewed": "2021-08-02T14:28:01.332+0000",
        "watches": {
            "self": "https://jira.example.com/rest/api/2/issue/PRJ-1/watchers",
            "watchCount": 3,
            "isWatching": false
        },
        "created": "2021-08-01T14:20:10.674+0000",
        "priority": {
            "self": "https://jira.example.com/rest/api/2/priority/3",
            "iconUrl": "https://jira.example.com/images/icons/priorities/major.png",
            "name": "Major",
            "id": "3"
        },
        "labels": [
            "api",
            "jira"
        ],
        "customfield_10018": {
            "hasEpicLinkFieldDependency": false,
            "showField": true,
            "nonEditableReason": {
                "reason": "NOT_EDITABLE",
                "message": "This field cannot be edited."
            }
        },
        "customfield_10019": "10031",
        "versions": [],
        "issuelinks": [],
        "assignee": {
            "self": "https://jira.example.com/rest/api/2/user?username=assignee",
            "accountId": "5b10ac8d82e05b22cc7d4ef5",
            "emailAddress": "assignee@example.com",
            "avatarUrls": {
                "48x48": "https://jira.example.com/secure/useravatar?ownerId=5b10ac8d82e05b22cc7d4ef5&avatarId=10346",
                "24x24": "https://jira.example.com/secure/useravatar?size=medium&ownerId=5b10ac8d82e05b22cc7d4ef5&avatarId=10346",
                "16x16": "https://jira.example.com/secure/useravatar?size=xsmall&ownerId=5b10ac8d82e05b22cc7d4ef5&avatarId=10346",
                "32x32": "https://jira.example.com/secure/useravatar?size=small&ownerId=5b10ac8d82e05b22cc7d4ef5&avatarId=10346"
            },
            "displayName": "Assignee Name",
            "active": true,
            "timeZone": "Australia/Sydney",
            "accountType": "atlassian"
        },
        "updated": "2021-08-02T14:28:01.332+0000",
        "status": {
            "self": "https://jira.example.com/rest/api/2/status/1",
            "description": "The issue is open and ready for the assignee to start work on it.",
            "iconUrl": "https://jira.example.com/images/icons/statuses/open.png",
            "name": "Open",
            "id": "1",
            "statusCategory": {
                "self": "https://jira.example.com/rest/api/2/statuscategory/2",
                "id": 2,
                "key": "new",
                "colorName": "blue-gray",
                "name": "To Do"
            }
        },
        "components": [],
        "customfield_10014": "10020",
        "summary": "This is a summary of the issue",
        "creator": {
            "self": "https://jira.example.com/rest/api/2/user?username=creator",
            "accountId": "5b109f2e9729b51b54dc274d",
            "emailAddress": "creator@example.com",
            "avatarUrls": {
                "48x48": "https://jira.example.com/secure/useravatar?ownerId=5b109f2e9729b51b54dc274d&avatarId=10343",
                "24x24": "https://jira.example.com/secure/useravatar?size=medium&ownerId=5b109f2e9729b51b54dc274d&avatarId=10343",
                "16x16": "https://jira.example.com/secure/useravatar?size=xsmall&ownerId=5b109f2e9729b51b54dc274d&avatarId=10343",
                "32x32": "https://jira.example.com/secure/useravatar?size=small&ownerId=5b109f2e9729b51b54dc274d&avatarId=10343"
            },
            "displayName": "Creator Name",
            "active": true,
            "timeZone": "Australia/Sydney",
            "accountType": "atlassian"
        },
        "subtasks": [],
        "reporter": {
            "self": "https://jira.example.com/rest/api/2/user?username=reporter",
            "accountId": "5b109f2e9729b51b54dc274d",
            "emailAddress": "reporter@example.com",
            "avatarUrls": {
                "48x48": "https://jira.example.com/secure/useravatar?ownerId=5b109f2e9729b51b54dc274d&avatarId=10343",
                "24x24": "https://jira.example.com/secure/useravatar?size=medium&ownerId=5b109f2e9729b51b54dc274d&avatarId=10343",
                "16x16": "https://jira.example.com/secure/useravatar?size=xsmall&ownerId=5b109f2e9729b51b54dc274d&avatarId=10343",
                "32x32": "https://jira.example.com/secure/useravatar?size=small&ownerId=5b109f2e9729b51b54dc274d&avatarId=10343"
            },
            "displayName": "Reporter Name",
            "active": true,
            "timeZone": "Australia/Sydney",
            "accountType": "atlassian"
        },
        "aggregateprogress": {
            "progress": 0,
            "total": 0
        },
        "customfield_10000": "10002",
        "progress": {
            "progress": 0,
            "total": 0
        },
        "votes": {
            "self": "https://jira.example.com/rest/api/2/issue/PRJ-1/votes",
            "votes": 0,
            "hasVoted": false
        }
    },
    "renderedFields": {
        "statuscategorychangedate": "2021-08-01T14:20:10.674+0000",
        "lastViewed": "2021-08-02T14:28:01.332+0000",
        "created": "2021-08-01T14:20:10.674+0000",
        "customfield_10017": "10019",
        "updated": "2021-08-02T14:28:01.332+0000",
        "description": "This is a description of the issue",
        "environment": "Testing environment"
    }
}

Use a unified ticketing API to integrate at scale

In this article, we walked through the process of getting tickets from Jira using Python. But what if your clients use a variety of ticketing systems?

You can integrate your product with all of your clients' ticketing solutions to sync tasks—among other types of data—by leveraging Merge, the leading Unified API solution.

Simply build to Merge's Ticketing and Project Management Unified API to offer 30+ ticketing integrations. You'll also be able to access Merge's robust Integrations Management features to identify and troubleshoot issues quickly and successfully.

You can learn more about Merge by scheduling a demo with one of our integration experts.