jira
jira is a community-maintained Python client for Jira's REST APIs. A JIRA session handles authentication and returns resource objects for issues, projects, users, comments, attachments, boards, sprints, service desks, and related entities, while convenience methods cover JQL searches, creates, updates, assignments, transitions, watchers, and worklogs. It aims to support Jira Cloud plus Server and Data Center, which is useful for shared automation but also exposes callers to product-version, authentication, field-schema, and permission differences.
The practical general-purpose Python client for substantial Jira automation, especially when resource helpers save real code. For small Cloud-only integrations or brand-new endpoints, direct REST calls can be easier to reason about and quicker to update.
Use it if
- You need broad Jira issue automation from Python and prefer resource objects over hand-written requests calls
- Your code must work with Jira Cloud and self-hosted Server or Data Center through one client where their APIs overlap
- You need JQL search, issue creation, comments, transitions, attachments, Agile boards, or service-desk operations
- You can integration-test against the exact Jira edition, fields, workflows, and permissions used in production
- You need an official Atlassian-supported Python SDK: the README explicitly describes this project as community maintained and warns that bugs or features may not be implemented quickly
- You only call two stable endpoints: direct requests code can be smaller and clearer than installing this client's dependency set and learning its dynamic Resource wrappers
- You need complete coverage of a newly released Jira Cloud API immediately: the project targets both Cloud and Server or Data Center, and its own README warns community fixes may lag
- You cannot test product-specific authentication and field shapes: Cloud uses email plus API token through basic_auth, self-hosted PATs use token_auth, and workflows, custom fields, user identifiers, and rich text differ by Jira configuration
- You run Python 3.9 or older: version 3.10.5 requires Python 3.10, while optional CLI, async, Kerberos, JWT, and file-type features add separate extras and system requirements
Setup reality
Version 3.10.5 requires Python 3.10 and installs requests, requests-oauthlib, requests-toolbelt, defusedxml, packaging, and typing_extensions. Authentication is the first trap. Jira Cloud normally uses basic_auth=(email, api_token); username and password authentication and cookie auth are no longer supported there. Jira Server or Data Center 8.14+ can use token_auth with a personal access token. OAuth 1.0a needs an access token, token secret, consumer key, and matching private key configured through an application link. Kerberos, JWT-related helpers, the interactive CLI, asynchronous requests, and libmagic-based content detection are optional extras, not base behavior; filemagic also needs the native libmagic library and is awkward on Windows. Store credentials in environment variables, a secret manager, or netrc, never source. Set the server URL explicitly because JIRA() with no URL points at the local Atlassian SDK test address. Then test permissions and schema against the target tenant: assignment permission differs from edit permission, transitions are workflow-specific and only currently available transitions are returned, required create fields vary by project, custom field IDs differ, Cloud user identity often uses accountId, and rich descriptions or comments may require Atlassian Document Format. JQL searches return a limited first page unless maxResults is set, and asking for only required fields reduces large payloads. The client supports both Cloud and self-hosted products, so not every method or payload works identically. Configure timeouts and retries for automation, handle JIRAError status codes, avoid logging credentials or issue contents, and integration-test create, update, transition, pagination, and attachment flows after Jira or package upgrades.
Patterns
Authenticate to Jira Cloud with an API tokenconnect-jira-cloud
import os
from jira import JIRA
jira = JIRA(
server=os.environ['JIRA_URL'],
basic_auth=(os.environ['JIRA_EMAIL'], os.environ['JIRA_API_TOKEN']),
)Cloud basic_auth is email plus API token, not an Atlassian account password; keep all three values out of source and logs.
Authenticate to self-hosted Jira with a PATconnect-data-center
jira = JIRA(
server=os.environ['JIRA_URL'],
token_auth=os.environ['JIRA_PAT'],
)The docs identify token_auth for Jira Server or Data Center personal access tokens, available from Jira Core 8.14.
Fetch only the issue fields you needread-issue-fields
issue = jira.issue('PROJ-123', fields='summary,status,assignee')
print(issue.fields.summary)
print(issue.fields.status.name)
assignee = issue.fields.assigneeRequesting a narrow field list avoids large issue payloads; optional fields such as assignee can be None.
Search issues with an explicit page sizesearch-with-jql
issues = jira.search_issues(
'project = PROJ AND statusCategory != Done ORDER BY priority DESC',
startAt=0,
maxResults=100,
fields='key,summary,priority,status',
)
for issue in issues:
print(issue.key, issue.fields.summary)The examples say search returns only the first 50 by default; paginate when the result can exceed maxResults.
Create an issue with explicit fieldscreate-issue
issue = jira.create_issue(fields={
'project': {'key': 'PROJ'},
'summary': 'Printer reports low toner',
'description': 'Observed on the third floor.',
'issuetype': {'name': 'Task'},
})
print(issue.key)Required fields and allowed issue types come from project configuration; custom fields use tenant-specific customfield IDs.
Update issue fields without notificationsupdate-issue
issue = jira.issue('PROJ-123')
issue.update(
notify=False,
fields={'summary': 'Updated summary'},
)Permission to edit does not imply permission to assign or transition, and notify behavior can vary with server capabilities.
Add a plain-text commentadd-comment
comment = jira.add_comment(
'PROJ-123',
'Deployment finished successfully.',
)Jira Cloud endpoints that expect Atlassian Document Format need a structured body rather than this plain string shape.
Find and perform an available transitiontransition-issue
issue = jira.issue('PROJ-123')
transitions = jira.transitions(issue)
done = next(t for t in transitions if t['name'].casefold() == 'done')
jira.transition_issue(issue, done['id'])Transition IDs and names are workflow-specific, and the server returns only transitions available to the current user and state.
Upload an attachment with a closed file handleupload-attachment
with open('report.txt', 'rb') as attachment:
jira.add_attachment(
issue='PROJ-123',
attachment=attachment,
filename='report.txt',
)The optional filemagic integration needs native libmagic; supplying a filename and file object avoids depending on path-only convenience behavior.
Handle REST failures without hiding detailshandle-jira-error
from jira.exceptions import JIRAError
try:
issue = jira.issue('PROJ-404')
except JIRAError as exc:
if exc.status_code == 404:
issue = None
else:
raiseA 404 can also hide a permission problem in Jira; log a safe request identifier, not tokens or full issue content.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| atlassian-python-api | PyPI | Your automation spans Confluence, Bitbucket, Bamboo, or other Atlassian products in addition to Jira |
| jiraone | PyPI | You target Jira Cloud and want higher-level helpers for reports, fields, and bulk operations |
| requests | PyPI | You use only a few Jira endpoints and want exact control over the documented REST payloads and upgrade timing |