How to get attachments from Jira using Python

Jira, a sophisticated project management tool created by Atlassian, facilitates the planning, tracking, and release of software, making it a favorite among numerous software development teams. Jira also boasts features like customizable workflows, extensive reporting tools, and a potent API, all of which empower businesses to optimize their project management and boost productivity.

Given the platform’s versatility, it stores a variety of valuable data—and attachments are no exception. Getting attachments can help teams automate and streamline various processes, such as issue examination or data backups. And by leveraging the Jira API, you can programmatically fetch these files as needed.

To help you get attachments from the Jira API, we’ll cover everything you need to know. This includes setting up authentication for the Jira API and performing get requests to fetch attachments.

Authenticating with Jira

To interact with the Jira API and retrieve attachments, you need to authenticate your requests. 

Jira uses Basic Authentication, which requires you to include a header with your request. 

This header should be in the format <code class='blog_inline-code'>Authorization: Basic {Email Address}:{API-KEY}</code>. 

You can generate the Base-64 encoded string of your email address and API key, then use this string in your authorization header. In addition, your email address and API key are the credentials Jira uses to authenticate your API request. You can find your API key—which is a secure, revocable key—, within your Atlassian account.

Related: What you need to do to get projects from the Jira API

Pulling attachments from Jira

The Python code to pull attachments from the Jira API involves two steps. 

First, we need to hit the search API endpoint and paginate through the response. Second, we’ll retrieve the details of each issue and include the attachments by using the issue API endpoint.

Hit the search API endpoint

Here is the Python code that accomplishes this:


import requests
import base64
import json
# Authentication
email = "YOUR_EMAIL"
api_key = "YOUR_API_KEY"
base64_user_pass = base64.b64encode(f"{email}:{api_key}".encode()).decode()
# Pagination
offset = 0
limit = 50
has_next_page = True
# Search API Endpoint
search_url = "https://{DOMAIN}.atlassian.net/rest/api/3/search"
headers = {
    "Authorization": f"Basic {base64_user_pass}",
    "Accept": "application/json"
}
while has_next_page:
    query = {
        "expand": "renderedFields",
        "maxResults": limit,
        "startAt": offset
    }
    response = requests.get(search_url, headers=headers, params=query)
    data = response.json()
    if 'issues' in data:
        for issue in data['issues']:
            print(issue['id'])
    else:
        has_next_page = False
    offset += limit

This script will loop through paginated issues and print each issue's ID to the console.

Hit the issue API endpoint

The next step is to get the attachments of each issue. You can do this by hitting the issue API endpoint for each issue ID:


# Issue API Endpoint
issue_url = "https://{DOMAIN}.atlassian.net/rest/api/3/issue/{id}"
for issue_id in issue_ids:
    response = requests.get(issue_url.format(id=issue_id), headers=headers)
    issue_data = response.json()
    if 'fields' in issue_data and 'attachment' in issue_data['fields']:
        for attachment in issue_data['fields']['attachment']:
            print(attachment['filename'])

This script will print the filename of each attachment in each issue to the console.

An example of an individual item returned by this API Endpoint is:

{
    "expand": "schema,names",
    "id": "10002",
    "self": "https://myinstance.atlassian.net/rest/api/2/issue/10002",
    "key": "PROJ-10002",
    "fields": {
        "statuscategorychangedate": "2019-07-02T12:32:49.134+0000",
        "issuetype": {
            "self": "https://myinstance.atlassian.net/rest/api/2/issuetype/5",
            "id": "5",
            "description": "The sub-task of the issue",
            "iconUrl": "https://myinstance.atlassian.net/secure/viewavatar?size=medium&avatarId=10318&avatarType=issuetype",
            "name": "Sub-task",
            "subtask": true,
            "avatarId": 10318,
            "entityId": "80ed3c99-57ff-4beb-9a24-31bdb051d650",
            "hierarchyLevel": 4
        },
        "timespent": null,
        "project": {
            "self": "https://myinstance.atlassian.net/rest/api/2/project/10001",
            "id": "10001",
            "key": "PROJ",
            "name": "Project",
            "simplified": false,
            "avatarUrls": {
                "48x48": "https://myinstance.atlassian.net/secure/projectavatar?pid=10001&avatarId=10412",
                "24x24": "https://myinstance.atlassian.net/secure/projectavatar?size=small&pid=10001&avatarId=10412",
                "16x16": "https://myinstance.atlassian.net/secure/projectavatar?size=xsmall&pid=10001&avatarId=10412",
                "32x32": "https://myinstance.atlassian.net/secure/projectavatar?size=medium&pid=10001&avatarId=10412"
            },
            "projectTypeKey": "software"
        },
        "fixVersions": [],
        "customfield_10018": {
            "hasEpicLinkFieldDependency": false,
            "showField": true,
            "nonEditableReason": {
                "reason": "NOT_EDITABLE",
                "message": "This field cannot be edited in the current transition."
            }
        },
        "customfield_10019": "Some value",
        "aggregatetimespent": null,
        "workratio": -1,
        "attachment": [
            {
                "self": "https://myinstance.atlassian.net/rest/api/2/attachment/10010",
                "id": "10010",
                "filename": "picture.jpg",
                "created": "2019-07-02T12:37:53.273+0000",
                "size": 23123,
                "mimeType": "image/jpeg",
                "content": "https://myinstance.atlassian.net/secure/attachment/10010/picture.jpg",
                "thumbnail": "https://myinstance.atlassian.net/secure/thumbnail/10010/picture.jpg",
                "author": {
                    "self": "https://myinstance.atlassian.net/rest/api/2/user?accountId=557058:2baf9c2d-6aad-4b09-a0b1-df776702a9e3",
                    "accountId": "557058:2baf9c2d-6aad-4b09-a0b1-df776702a9e3",
                    "avatarUrls": {
                        "48x48": "https://myinstance.atlassian.net/secure/useravatar?avatarId=10346",
                        "24x24": "https://myinstance.atlassian.net/secure/useravatar?size=small&avatarId=10346",
                        "16x16": "https://myinstance.atlassian.net/secure/useravatar?size=xsmall&avatarId=10346",
                        "32x32": "https://myinstance.atlassian.net/secure/useravatar?size=medium&avatarId=10346"
                    },
                    "displayName": "User Name",
                    "active": true,
                    "timeZone": "America/Los_Angeles",
                    "accountType": "atlassian"
                }
            }
        ],
        "lastViewed": "2019-07-02T12:37:57.802+0000"
    }
}

Related: What you need to do to fetch comments from Jira (using Python)

Testing your Jira integration

It's a common scenario where everything seems fine during the development and testing phases, but when you go live in the production environment, unexpected issues  arise. These problems could stem from network instability, data format discrepancies, code conflicts, among other factors.

To proactively identify and address potential issues, here's what you can do:

  • Create a test plan: This plan should outline what you aim to achieve with each test, ensuring thorough coverage of all integration aspects. For example, you might include tests for user authentication or data syncing.
  • Performance testing: This step is crucial to ensure your integration can handle real-world demands. Include tests like load testing, where you simulate a high number of users, or stress testing, to see how the system performs under extreme conditions.
  • Use the right tools: Choosing the best tools can significantly enhance your testing effectiveness. Consider tools like JMeter for performance testing or SoapUI for API testing, which can provide comprehensive testing capabilities.
  • Validate JSON and XML schemas: Ensuring that your JSON and XML schemas are correct is vital for seamless data exchange. You can do this by using schema validators to check for any inconsistencies or errors in your data formats.
  • Security testing: Last but not least, security testing is paramount. This involves checking for vulnerabilities that could be exploited once the integration is live, ensuring that both your data and the system are safeguarded against potential threats.

Final thoughts

In the diverse world of ticketing systems, Jira is hardly the only player. There's a whole universe of other ticketing applications out there, such as Asana, ClickUp, and Zendesk. 

Seamlessly integrate with the ticketing systems your clients use at scale by leveraging Merge's Ticketing Unified API. You can learn more about Merge and its Ticketing Unified API by scheduling a demo with one of our integration experts.

But Merge isn’t just a Unified 
API product. Merge is an integration platform to also manage customer integrations.  gradient text
“It was the same process, go talk to their team, figure out their API. It was taking a lot of time. And then before we knew it, there was a laundry list of HR integrations being requested for our prospects and customers.” gradient text
“It was the same process, go talk to their team, figure out their API. It was taking a lot of time. And then before we knew it, there was a laundry list of HR integrations being requested for our prospects and customers.” gradient text
“It was the same process, go talk to their team, figure out their API. It was taking a lot of time. And then before we knew it, there was a laundry list of HR integrations being requested for our prospects and customers.” gradient text

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6
“It was the same process, go talk to their team, figure out their API. It was taking a lot of time. And then before we knew it, there was a laundry list of HR integrations being requested for our prospects and customers.”

Daniel Marashlian - Co-Founder & CTO

This is a link inside of a rich text

  • List item
  • List item
  • List item
  1. List item
  2. List item
  3. List item
Caption goes here
This is some text inside of a div block.
Table of contents
Add hundreds of integrations to your product through Merge’s Unified API
Get a demo

How to get attachments from Jira using Python

Jon Gitlin
Senior Content Marketing Manager
@Merge

Jira, a sophisticated project management tool created by Atlassian, facilitates the planning, tracking, and release of software, making it a favorite among numerous software development teams. Jira also boasts features like customizable workflows, extensive reporting tools, and a potent API, all of which empower businesses to optimize their project management and boost productivity.

Given the platform’s versatility, it stores a variety of valuable data—and attachments are no exception. Getting attachments can help teams automate and streamline various processes, such as issue examination or data backups. And by leveraging the Jira API, you can programmatically fetch these files as needed.

To help you get attachments from the Jira API, we’ll cover everything you need to know. This includes setting up authentication for the Jira API and performing get requests to fetch attachments.

Authenticating with Jira

To interact with the Jira API and retrieve attachments, you need to authenticate your requests. 

Jira uses Basic Authentication, which requires you to include a header with your request. 

This header should be in the format <code class='blog_inline-code'>Authorization: Basic {Email Address}:{API-KEY}</code>. 

You can generate the Base-64 encoded string of your email address and API key, then use this string in your authorization header. In addition, your email address and API key are the credentials Jira uses to authenticate your API request. You can find your API key—which is a secure, revocable key—, within your Atlassian account.

Related: What you need to do to get projects from the Jira API

Pulling attachments from Jira

The Python code to pull attachments from the Jira API involves two steps. 

First, we need to hit the search API endpoint and paginate through the response. Second, we’ll retrieve the details of each issue and include the attachments by using the issue API endpoint.

Hit the search API endpoint

Here is the Python code that accomplishes this:


import requests
import base64
import json
# Authentication
email = "YOUR_EMAIL"
api_key = "YOUR_API_KEY"
base64_user_pass = base64.b64encode(f"{email}:{api_key}".encode()).decode()
# Pagination
offset = 0
limit = 50
has_next_page = True
# Search API Endpoint
search_url = "https://{DOMAIN}.atlassian.net/rest/api/3/search"
headers = {
    "Authorization": f"Basic {base64_user_pass}",
    "Accept": "application/json"
}
while has_next_page:
    query = {
        "expand": "renderedFields",
        "maxResults": limit,
        "startAt": offset
    }
    response = requests.get(search_url, headers=headers, params=query)
    data = response.json()
    if 'issues' in data:
        for issue in data['issues']:
            print(issue['id'])
    else:
        has_next_page = False
    offset += limit

This script will loop through paginated issues and print each issue's ID to the console.

Hit the issue API endpoint

The next step is to get the attachments of each issue. You can do this by hitting the issue API endpoint for each issue ID:


# Issue API Endpoint
issue_url = "https://{DOMAIN}.atlassian.net/rest/api/3/issue/{id}"
for issue_id in issue_ids:
    response = requests.get(issue_url.format(id=issue_id), headers=headers)
    issue_data = response.json()
    if 'fields' in issue_data and 'attachment' in issue_data['fields']:
        for attachment in issue_data['fields']['attachment']:
            print(attachment['filename'])

This script will print the filename of each attachment in each issue to the console.

An example of an individual item returned by this API Endpoint is:

{
    "expand": "schema,names",
    "id": "10002",
    "self": "https://myinstance.atlassian.net/rest/api/2/issue/10002",
    "key": "PROJ-10002",
    "fields": {
        "statuscategorychangedate": "2019-07-02T12:32:49.134+0000",
        "issuetype": {
            "self": "https://myinstance.atlassian.net/rest/api/2/issuetype/5",
            "id": "5",
            "description": "The sub-task of the issue",
            "iconUrl": "https://myinstance.atlassian.net/secure/viewavatar?size=medium&avatarId=10318&avatarType=issuetype",
            "name": "Sub-task",
            "subtask": true,
            "avatarId": 10318,
            "entityId": "80ed3c99-57ff-4beb-9a24-31bdb051d650",
            "hierarchyLevel": 4
        },
        "timespent": null,
        "project": {
            "self": "https://myinstance.atlassian.net/rest/api/2/project/10001",
            "id": "10001",
            "key": "PROJ",
            "name": "Project",
            "simplified": false,
            "avatarUrls": {
                "48x48": "https://myinstance.atlassian.net/secure/projectavatar?pid=10001&avatarId=10412",
                "24x24": "https://myinstance.atlassian.net/secure/projectavatar?size=small&pid=10001&avatarId=10412",
                "16x16": "https://myinstance.atlassian.net/secure/projectavatar?size=xsmall&pid=10001&avatarId=10412",
                "32x32": "https://myinstance.atlassian.net/secure/projectavatar?size=medium&pid=10001&avatarId=10412"
            },
            "projectTypeKey": "software"
        },
        "fixVersions": [],
        "customfield_10018": {
            "hasEpicLinkFieldDependency": false,
            "showField": true,
            "nonEditableReason": {
                "reason": "NOT_EDITABLE",
                "message": "This field cannot be edited in the current transition."
            }
        },
        "customfield_10019": "Some value",
        "aggregatetimespent": null,
        "workratio": -1,
        "attachment": [
            {
                "self": "https://myinstance.atlassian.net/rest/api/2/attachment/10010",
                "id": "10010",
                "filename": "picture.jpg",
                "created": "2019-07-02T12:37:53.273+0000",
                "size": 23123,
                "mimeType": "image/jpeg",
                "content": "https://myinstance.atlassian.net/secure/attachment/10010/picture.jpg",
                "thumbnail": "https://myinstance.atlassian.net/secure/thumbnail/10010/picture.jpg",
                "author": {
                    "self": "https://myinstance.atlassian.net/rest/api/2/user?accountId=557058:2baf9c2d-6aad-4b09-a0b1-df776702a9e3",
                    "accountId": "557058:2baf9c2d-6aad-4b09-a0b1-df776702a9e3",
                    "avatarUrls": {
                        "48x48": "https://myinstance.atlassian.net/secure/useravatar?avatarId=10346",
                        "24x24": "https://myinstance.atlassian.net/secure/useravatar?size=small&avatarId=10346",
                        "16x16": "https://myinstance.atlassian.net/secure/useravatar?size=xsmall&avatarId=10346",
                        "32x32": "https://myinstance.atlassian.net/secure/useravatar?size=medium&avatarId=10346"
                    },
                    "displayName": "User Name",
                    "active": true,
                    "timeZone": "America/Los_Angeles",
                    "accountType": "atlassian"
                }
            }
        ],
        "lastViewed": "2019-07-02T12:37:57.802+0000"
    }
}

Related: What you need to do to fetch comments from Jira (using Python)

Testing your Jira integration

It's a common scenario where everything seems fine during the development and testing phases, but when you go live in the production environment, unexpected issues  arise. These problems could stem from network instability, data format discrepancies, code conflicts, among other factors.

To proactively identify and address potential issues, here's what you can do:

  • Create a test plan: This plan should outline what you aim to achieve with each test, ensuring thorough coverage of all integration aspects. For example, you might include tests for user authentication or data syncing.
  • Performance testing: This step is crucial to ensure your integration can handle real-world demands. Include tests like load testing, where you simulate a high number of users, or stress testing, to see how the system performs under extreme conditions.
  • Use the right tools: Choosing the best tools can significantly enhance your testing effectiveness. Consider tools like JMeter for performance testing or SoapUI for API testing, which can provide comprehensive testing capabilities.
  • Validate JSON and XML schemas: Ensuring that your JSON and XML schemas are correct is vital for seamless data exchange. You can do this by using schema validators to check for any inconsistencies or errors in your data formats.
  • Security testing: Last but not least, security testing is paramount. This involves checking for vulnerabilities that could be exploited once the integration is live, ensuring that both your data and the system are safeguarded against potential threats.

Final thoughts

In the diverse world of ticketing systems, Jira is hardly the only player. There's a whole universe of other ticketing applications out there, such as Asana, ClickUp, and Zendesk. 

Seamlessly integrate with the ticketing systems your clients use at scale by leveraging Merge's Ticketing Unified API. You can learn more about Merge and its Ticketing Unified API by scheduling a demo with one of our integration experts.

“It was the same process, go talk to their team, figure out their API. It was taking a lot of time. And then before we knew it, there was a laundry list of HR integrations being requested for our prospects and customers.”

Name
Position
Jon Gitlin
Senior Content Marketing Manager
@Merge

Jon Gitlin is the Managing Editor of Merge's blog. He has several years of experience in the integration and automation space; before Merge, he worked at Workato, an integration platform as a service (iPaaS) solution, where he also managed the company's blog. In his free time he loves to watch soccer matches, go on long runs in parks, and explore local restaurants.

Read more

How to GET tasks from the Asana API in Python

Engineering

How to GET folders from the Dropbox API in Python

Engineering

10 critical REST API interview questions for 2025—answered

Engineering

Subscribe to the Merge Blog

Subscribe to the Merge Blog

Subscribe

Make integrations your competitive advantage

Stay in touch to learn how Merge can unlock hundreds of integrations in days, not years

Get a demo

Make integrations your competitive advantage

Stay in touch to learn how Merge can unlock hundreds of integrations in days, not years

Get a demo

Get our best content every week

Our weekly newsletter provides the best practices you need to build high performing product integrations.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.