Bitbucket Unofficial SDK
Unofficial. This project is not affiliated with, endorsed by, or supported by Atlassian. "Bitbucket" is a trademark of Atlassian. Use at your own risk against the Bitbucket Cloud REST API.
Typed, synchronous Python SDK for the Bitbucket Cloud REST API: repository
core (CRUD, forks, hooks, permissions), refs (branches/tags), source, commits,
downloads, and the full pull-request surface (comments, statuses, tasks,
merging, default reviewers). Every response is a validated, frozen
pydantic model with attribute access and real
Python types — not a raw dict.
See docs/architecture/ for how the client is
layered.
Table of contents
- Installation
- Requirements
- Usage
- Configuration
- Security
- Platform notes
- Origin
- Open items
- Contributing
Installation
Install it from PyPI:
uv add bitbucket-unofficial-sdk
or with pip install bitbucket-unofficial-sdk. To try an unreleased commit,
install it from git instead:
uv add "bitbucket-unofficial-sdk @ git+https://github.com/gajaguar/bitbucket-sdk"
Requirements
- mise — pins the toolchain (
mise.toml: Python 3.14, uv, node, pnpm, pre-commit, checkmake); runmise install, thenmake install - A Bitbucket Cloud API token and the email of its Atlassian account — see Authentication
Usage
make install # sync Python deps, install node tooling, install pre-commit hook
make check # read-only gate: lint, format-check, mypy, pyright,
# md-lint, spell, pylint
make fix # apply safe auto-fixes (format, ruff --fix, markdownlint --fix)
make test # run the test suite
Run make help for the full target list.
Authentication
from bitbucket import BitbucketClient
# reads ATLASSIAN_USER_EMAIL and ATLASSIAN_API_TOKEN from the environment
client = BitbucketClient()
# or pass them explicitly; an explicit argument takes precedence
client = BitbucketClient(email="...", api_token="...")
# api_token also accepts a zero-argument callable, invoked lazily on every
# request instead of once at construction time — useful for a rotating or
# externally-managed token (e.g. one read from an OS keyring by the calling
# application).
client = BitbucketClient(email="...", api_token=lambda: keychain.current_token())
The credential sources, in order of precedence, are:
- The
api_tokenargument as a provider (a callable). - The
emailandapi_tokenarguments as strings. - The
ATLASSIAN_USER_EMAILandATLASSIAN_API_TOKENenvironment variables.
ATLASSIAN_API_KEY is the former name of ATLASSIAN_API_TOKEN. It still
works, with a DeprecationWarning, when ATLASSIAN_API_TOKEN is not set.
Getting a credential
Bitbucket Cloud authenticates with the email of your Atlassian account and an API token. Atlassian removed app passwords on 2026-07-28, so they no longer work.
- Open your profile menu and choose Account settings.
- Open the Security tab and choose Create and manage API tokens.
- Choose Create API token with scopes, then set a name and an expiry date.
- Choose Bitbucket as the app and select the scopes your code needs. Bitbucket rejects a token that has no Bitbucket scopes.
- Copy the token. Atlassian shows it only once.
The token is bound to your account and expires on the date you chose, so rotate it before then.
What the SDK does not do
The SDK only reads the sources listed above. It has no OS keyring or keychain
integration, no password-manager support, no OAuth flow, and no interactive
prompts — that is an application-level concern for whatever consumes this SDK.
Bearer (OAuth 2.0) support is planned; see docs/api/roadmap.md.
The full contract is in
docs/sdk/credential-contract.md, and the
values that belong to Bitbucket are in
docs/sdk/credentials.md.
Recipes
List a repository's open pull requests, fetch one's diff, and post a comment:
from bitbucket import BitbucketClient
from bitbucket import CommentContentCreate
from bitbucket import CommentCreate
with BitbucketClient() as client:
# workspace() takes an explicit slug; default_workspace() reads
# BITBUCKET_WORKSPACE from the environment instead.
repository = client.default_workspace().repository("my-repo")
open_prs = list(repository.pull_requests.list(state="OPEN"))
for pull_request in open_prs:
print(pull_request.id, pull_request.title)
diff = repository.pull_requests.diff(open_prs[0].id)
repository.pull_requests.comments(open_prs[0].id).create(
CommentCreate(content=CommentContentCreate(raw="Looks good.")),
)
Every list() method returns a lazy iterator that follows Bitbucket's next
cursor; iterate it directly, wrap in list(...), or call list_page(cursor=...)
to manage pagination yourself.
Merge a pull request and wait for the result — POST .../merge returns
202 Accepted with an async task to poll, not the merged PR directly:
from bitbucket import MergeParameters
status = repository.pull_requests.merge_and_wait(
open_prs[0].id,
MergeParameters(merge_strategy="squash"),
)
print(status.task_status)
merge() returns the task immediately without waiting; poll it yourself with
merge_task_status(pull_request_id, task_id) if you need finer control.
Create a repository, push a branch, and read a file from it:
from bitbucket import BranchCreate
from bitbucket import RefTargetSpec
from bitbucket import RepositoryCreate
with BitbucketClient() as client:
workspace = client.default_workspace()
repository = workspace.repositories.create("new-repo", RepositoryCreate(is_private=True))
main = next(iter(workspace.repository("new-repo").refs.branches.list()))
workspace.repository("new-repo").refs.branches.create(
BranchCreate(name="feature", target=RefTargetSpec(hash=main.target.hash)),
)
readme = workspace.repository("new-repo").source.read(main.target.hash, "README.md")
Project layout
.
├── pyproject.toml # deps, ruff/mypy/pyright/pytest/coverage config
├── Makefile # check/fix command surface (root: shared targets)
├── mk/python.mk # Python-specific targets, wired into the Makefile
├── mise.toml # pinned toolchain versions
├── docs/ # OKF notes: architecture, endpoint coverage, SDK
├── src/bitbucket/ # SDK package (client, models/, resources/)
└── tests/
├── unit/ # respx-backed, offline, deterministic
└── live/ # marker-gated (`-m live`), hits a real workspace
Configuration
| Variable | Where it's read | Default | Description |
|---|---|---|---|
ATLASSIAN_USER_EMAIL |
BitbucketClient() |
none, required | Email of the Atlassian account, used when email is not passed. |
ATLASSIAN_API_TOKEN |
BitbucketClient() |
none, required | API token, used when api_token is not passed. |
ATLASSIAN_API_KEY |
BitbucketClient() |
none | Deprecated name of ATLASSIAN_API_TOKEN; emits a warning. |
BITBUCKET_WORKSPACE |
BitbucketClient.default_workspace() |
none | Workspace slug used by default_workspace(). |
The names are exported as EMAIL_ENV_VAR, API_TOKEN_ENV_VAR and
WORKSPACE_ENV_VAR, so an application does not have to repeat the strings.
Security
A Bitbucket API token grants the access of its scopes to everything your
account can reach — never commit one, and rotate it immediately if it is
exposed. The SDK never logs headers or its configuration, and the token is
excluded from repr(ClientConfig).
Platform notes
- CI runs the full gate.
python.ymlrunsmake checkandmake teston every push tomainand every pull request. requires-python = ">=3.14"excludes most current Python installations (3.11–3.13); this is a deliberate, revisitable floor.mise.tomlforcesuvonto the mise-provided interpreter through its[env](UV_PYTHON_PREFERENCE=only-system,UV_PYTHON_DOWNLOADS=never), so.python-versionis intentionally absent — mise is the single source of truth for the pinned Python version.
Origin
Extracted from a larger internal toolkit's Bitbucket provider module, which had grown the whole project's maintenance surface. That toolkit keeps the provider-neutral pull-request seam (models, protocol, registry) and review-workflow logic (comment filtering and threading, reviewer-queue aggregation); this SDK carries only the Bitbucket wire client.
Open items
- Branch restrictions, branching model, projects, workspaces, and pipelines
are not yet modeled — see
docs/api/endpoint-coverage.mdfor the full endpoint matrix anddocs/api/roadmap.mdfor the phased plan to full API parity. - A handful of
Commitsoperations from the original Phase 2 estimate (a "file-conflicts" endpoint and up to 2 others) couldn't be confidently mapped to a real spec path without re-checking the live spec — see the note indocs/api/endpoint-coverage.md'sCommitssection.
Contributing
See CONTRIBUTING.md.
Metadata
Release files for bitbucket-unofficial-sdk 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bitbucket_unofficial_sdk-0.4.1.tar.gz | 128.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bitbucket_unofficial_sdk-0.4.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 178.4 kB
Release files / bitbucket_unofficial_sdk-0.4.1.tar.gz
| Download URL | bitbucket_unofficial_sdk-0.4.1.tar.gz |
|---|---|
| Size | 128.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
25fbb177d451ef4f47f0ce204ea6f87f8c73da8b7df7873918d800cc2ed7a828
|
|
BLAKE2b-256 checksum How to use checksums |
321a0967bdbf1afab5b50a6486df391f9b6c5d5f3cb5f0aa47a89e5eb284f8e3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency logRelease files / bitbucket_unofficial_sdk-0.4.1-py3-none-any.whl
| Download URL | bitbucket_unofficial_sdk-0.4.1-py3-none-any.whl |
|---|---|
| Size | 50.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2e497e1ec97220563b92ef98741c9d245dd4dc6faa80ac8003f29bef7d46f5b3
|
|
BLAKE2b-256 checksum How to use checksums |
6fa4eab0bb9b6ac1f3bbe1c4f02e5adf0a148de95bb315ae702da9f8eab0e2d1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency log