atlassian-cli is an independent, community open-source project, not affiliated with, endorsed by, or sponsored by Atlassian, and is not Atlassian's official CLI (acli). Product names are used only to identify compatibility.

What is the Jira REST API?

The Jira REST API is the HTTP interface Atlassian exposes so you can read and change Jira data programmatically: issues, projects, transitions, comments, and more. Anything you can do in the Jira web UI, you can do with an HTTP request. For Jira Cloud, every call goes to a URL under https://your-domain.atlassian.net/rest/api/3/, where your-domain is your site name.

Two versions are live for Cloud. Version 3 (/rest/api/3/) is the current one and represents rich-text fields, like an issue description, in Atlassian Document Format (ADF), a nested JSON structure. Version 2 (/rest/api/2/) is still supported and accepts plain wiki markup for those same fields, which is often easier for quick scripts. Both versions share the same resource paths; the main difference is how text fields are encoded.

This guide is the raw-API reference: how to authenticate, the endpoints worth knowing, working curl commands, and how pagination behaves. At the end, we map those same calls to short commands so you do not have to hand-write JSON for routine work. If you would rather skip the HTTP entirely, the Jira API from the command line post starts from the CLI instead.

Server / Data Center note: self-managed Jira uses /rest/api/2/ and a different base path (your own host). The endpoint names below are the same, but authentication and ADF handling differ. The examples here target Jira Cloud.

Authentication and API tokens

For scripts and terminal work, the simplest option is HTTP Basic authentication using your Atlassian account email plus an API token as the password. Do not use your real account password: create a dedicated token instead.

  1. Go to id.atlassian.com and open the Security section of your account.
  2. Choose Create API token, give it a label, and copy the value. You only see it once.
  3. Store it in an environment variable so it never lands in your shell history or a committed file.
# Keep the token out of your command history
export JIRA_API_TOKEN="your-api-token-here"

# Verify auth by fetching the current user
curl -u "you@example.com:$JIRA_API_TOKEN" \
  -H "Accept: application/json" \
  "https://your-domain.atlassian.net/rest/api/3/myself"

The -u flag tells curl to send Basic auth; it base64-encodes email:token into the Authorization header for you. A 200 with your account JSON means auth works. A 401 means the email or token is wrong; a 403 usually means the token is valid but lacks permission for that resource.

For applications acting on behalf of other users, use OAuth 2.0 (3LO) rather than Basic auth. That flow issues short-lived access tokens scoped to specific permissions and is the right choice for anything user-facing. For your own automation and one-off scripts, an API token is enough.

Core endpoints you actually use

Jira Cloud exposes hundreds of endpoints, but a handful cover most day-to-day work. Here are the ones you will reach for first, with the HTTP method each expects.

Task Method Endpoint
Get one issue GET /rest/api/3/issue/{issueIdOrKey}
Search with JQL POST /rest/api/3/search/jql
Create an issue POST /rest/api/3/issue
Update an issue PUT /rest/api/3/issue/{issueIdOrKey}
List / apply transitions GET / POST /rest/api/3/issue/{issueIdOrKey}/transitions
Assign an issue PUT /rest/api/3/issue/{issueIdOrKey}/assignee
List projects GET /rest/api/3/project/search
List fields (find custom IDs) GET /rest/api/3/field

Two things trip people up here. First, in Jira Cloud you reference users by their accountId, not by username or email, a GDPR-driven change. Second, custom fields are addressed by an ID like customfield_10010, which you discover from the /field endpoint. Both details are easy to miss when you are copying an old Server-era snippet.

curl examples: read, search, create, transition

Every example below assumes you have exported JIRA_API_TOKEN as shown above. Replace your-domain, the project key, and the issue key with your own.

Read a single issue

curl -u "you@example.com:$JIRA_API_TOKEN" \
  -H "Accept: application/json" \
  "https://your-domain.atlassian.net/rest/api/3/issue/DEV-123?fields=summary,status,assignee"

The fields query parameter trims the response to just the fields you care about. Without it, Jira returns the full issue, which is large. Add expand=renderedFields if you want HTML-rendered text instead of raw ADF.

Search with JQL

The current search endpoint takes a POST body containing your JQL query, the fields to return, and a page size:

curl -u "you@example.com:$JIRA_API_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST \
  --data '{
    "jql": "project = DEV AND status = \"In Progress\" ORDER BY created DESC",
    "maxResults": 50,
    "fields": ["summary", "status", "assignee"]
  }' \
  "https://your-domain.atlassian.net/rest/api/3/search/jql"

Create an issue (ADF description)

This is where v3 gets verbose. The description field must be an ADF document, not a string:

curl -u "you@example.com:$JIRA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -X POST \
  --data '{
    "fields": {
      "project": { "key": "DEV" },
      "issuetype": { "name": "Task" },
      "summary": "Investigate login 500 errors",
      "description": {
        "type": "doc",
        "version": 1,
        "content": [
          { "type": "paragraph",
            "content": [ { "type": "text", "text": "Users report a 500 on login." } ] }
        ]
      }
    }
  }' \
  "https://your-domain.atlassian.net/rest/api/3/issue"

If that ADF block looks like a lot of ceremony for one sentence, you are not alone. Switching to /rest/api/2/ lets you pass "description": "Users report a 500 on login." as plain text. The trade-off is that v2 uses wiki markup rather than ADF for formatting.

List and apply a transition

Transition IDs are workflow-specific, so fetch the available ones first, then POST the ID you want:

# 1. See which transitions are available for this issue
curl -u "you@example.com:$JIRA_API_TOKEN" \
  -H "Accept: application/json" \
  "https://your-domain.atlassian.net/rest/api/3/issue/DEV-123/transitions"

# 2. Apply one by its numeric id (e.g. 31 = Done)
curl -u "you@example.com:$JIRA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -X POST \
  --data '{ "transition": { "id": "31" } }' \
  "https://your-domain.atlassian.net/rest/api/3/issue/DEV-123/transitions"

A successful transition returns 204 No Content, an empty body. That surprises people expecting the updated issue back; fetch the issue again if you need its new state.

Pagination and rate limits

The JQL search endpoint uses token-based pagination. Instead of asking for an offset, you read a nextPageToken from each response and pass it back to get the next page. When the token is missing, you have reached the end.

# First page: no token
curl -u "you@example.com:$JIRA_API_TOKEN" \
  -H "Content-Type: application/json" -X POST \
  --data '{ "jql": "project = DEV ORDER BY created DESC", "maxResults": 100, "fields": ["key"] }' \
  "https://your-domain.atlassian.net/rest/api/3/search/jql"

# Response contains "nextPageToken". Send it back for page two:
curl -u "you@example.com:$JIRA_API_TOKEN" \
  -H "Content-Type: application/json" -X POST \
  --data '{ "jql": "project = DEV ORDER BY created DESC", "maxResults": 100, "nextPageToken": "<token-from-previous-response>" }' \
  "https://your-domain.atlassian.net/rest/api/3/search/jql"

Older list endpoints (projects, comments, and similar) still use offset pagination with startAt and maxResults parameters and return a total count. Token pagination does not report a total, so you loop until there is no next token rather than computing page counts up front.

On rate limits, Jira Cloud uses a cost-based model and returns HTTP 429 when you exceed it, along with a Retry-After header telling you how many seconds to wait. Any robust script should read that header and back off rather than retrying immediately. Writing that retry loop by hand for every script is exactly the kind of boilerplate a wrapper removes.

A friendlier wrapper: atlassian-cli

curl is perfect for exploring the API and for one-off calls. But once you are building anything repeatable, the same friction shows up every time: encoding ADF, threading pagination tokens, handling 429 backoff, and parsing JSON out of the response. atlassian-cli is a free, MIT-licensed Rust binary that wraps these endpoints so a whole request collapses to one command.

How this differs from Atlassian's own CLI: atlassian-cli is an independent, community open-source project. It is not Atlassian's official CLI (acli) and is not affiliated with, endorsed by, or sponsored by Atlassian. Use the official acli for first-party vendor support; use atlassian-cli if you want a single binary spanning Jira, Confluence, Bitbucket, and Jira Service Management with JSON, CSV, and YAML output built in.

The mapping from raw endpoint to command is direct:

Task REST API atlassian-cli
Get an issue GET /issue/DEV-123 jira issue get DEV-123
Search POST /search/jql jira issue search --jql "..."
Create POST /issue jira issue create ...
Transition POST /issue/DEV-123/transitions jira issue transition DEV-123 --transition "Done"
Assign PUT /issue/DEV-123/assignee jira issue assign DEV-123 --assignee user@example.com

You authenticate once, then the token, base URL, ADF encoding, and pagination are handled for you. The create command that needed a full ADF block above becomes a single line, and search returns clean JSON you can pipe straight into jq:

# One-time login stores the profile; the API token is the same one from above
atlassian-cli auth login --profile work \
  --base-url https://your-domain.atlassian.net \
  --email you@example.com --token "$JIRA_API_TOKEN" --default

# Create an issue: plain text, no ADF ceremony
atlassian-cli jira issue create --project DEV --issue-type Task \
  --summary "Investigate login 500 errors"

# Search and get JSON out, pagination handled automatically
atlassian-cli jira issue search \
  --jql "project = DEV AND status = 'In Progress'" \
  --format json | jq '.[].key'

# Transition by name instead of hunting for a numeric id
atlassian-cli jira issue transition DEV-123 --transition "Done"

Notice the transition command takes the name "Done" directly: no separate call to look up transition ID 31. The CLI resolves it against the issue's workflow for you. Every command also accepts --format json|csv|yaml, so the same call can feed a spreadsheet, a script, or another tool without post-processing. See the full command reference for the complete surface, and the authentication guide for managing multiple profiles.

Neither approach is universally better. Reach for raw curl when you are debugging, calling an endpoint the CLI does not cover, or embedding a single request in an existing program. Reach for the CLI when you are automating routine Jira work and do not want to re-solve auth, ADF, and pagination each time. For a broader tour of managing Jira from a terminal, the complete Jira CLI guide covers projects, sprints, and bulk operations.

Try atlassian-cli

A single free binary for Jira, Confluence, Bitbucket, and JSM. Skip the boilerplate.

Install atlassian-cli

FAQ

What is the base URL for the Jira Cloud REST API?

For Jira Cloud, the base URL is https://your-domain.atlassian.net/rest/api/3/, where your-domain is your site name. Version 3 is the current REST API for Cloud and uses the Atlassian Document Format for rich-text fields; version 2 is still available at /rest/api/2/ and accepts plain wiki markup for those fields.

How do I authenticate with the Jira REST API?

The simplest method for scripts is HTTP Basic auth using your Atlassian account email and an API token as the password. Create a token at id.atlassian.com under Security, then pass it to curl with -u "you@example.com:$JIRA_API_TOKEN". For apps acting on behalf of users, use OAuth 2.0 (3LO) instead. Never use your account password directly.

Why does my Jira description field fail with the v3 REST API?

The v3 API expects rich-text fields such as description and comment bodies in Atlassian Document Format (ADF), a nested JSON structure, not a plain string. Sending a plain string returns a 400 error. Either build the ADF document, or switch to the v2 API which accepts plain text for those fields. A CLI wrapper hides this by accepting plain text and building the ADF for you.

How do I paginate results from the Jira REST API?

The current JQL search endpoint, POST /rest/api/3/search/jql, uses token-based pagination. Each response includes a nextPageToken when more results exist; pass it back in your next request to fetch the following page, and stop when the token is absent. Older list endpoints still use startAt and maxResults offsets with a total count.

Do I need to write code to use the Jira REST API?

No. You can call every endpoint with curl from a terminal or a shell script, which is enough for quick tasks and automation. For repeated work, a wrapper like atlassian-cli maps the same endpoints to short commands, handles authentication, pagination, and ADF for you, and returns JSON, CSV, or YAML directly.

Related Resources