How to Download All Repositories from a Bitbucket Workspace at Once
Need to clone every repo from your Bitbucket workspace? Here are 4 methods — from a simple Bash one-liner to Python scripts and GUI tools — to bulk clone all Bitbucket repositories in minutes.
Why You'd Want to Clone Everything
Whether you're migrating from Bitbucket to GitHub, creating a backup, or setting up a new machine — cloning repositories one by one is painful when your workspace has 50+ repos.
Here are 4 methods to download all repositories from a Bitbucket workspace at once, from simplest to most powerful.
Before You Start: Create an App Password
All methods below require authentication. Create an App Password in Bitbucket:
- Go to Bitbucket → Personal Settings → App Passwords
- Click Create app password
- Give it a label (e.g., "Bulk Clone")
- Select permissions: Repositories → Read
- Copy the generated password — you'll use it below
Your credentials:
Username: your-bitbucket-username
App Password: xxxx-xxxx-xxxx-xxxx
Workspace: your-workspace-slug
Note: The workspace slug is the lowercase name in your Bitbucket URL:
bitbucket.org/your-workspace-slug/
Method 1: Bash One-Liner (Fastest)
If you have curl, jq, and git installed, this single command clones every repo:
# Clone all repos from a Bitbucket workspace
curl -s -u "USERNAME:APP_PASSWORD" \
"https://api.bitbucket.org/2.0/repositories/WORKSPACE?pagelen=100" \
| jq -r '.values[].links.clone[] | select(.name=="https") | .href' \
| while read repo; do git clone "$repo"; doneReplace:
USERNAME— your Bitbucket usernameAPP_PASSWORD— the app password you createdWORKSPACE— your workspace slug
Handle Pagination (100+ repos)
The Bitbucket API returns max 100 repos per page. For larger workspaces:
#!/bin/bash
USERNAME="your-username"
APP_PASSWORD="your-app-password"
WORKSPACE="your-workspace"
CLONE_DIR="./bitbucket-repos"
mkdir -p "$CLONE_DIR"
cd "$CLONE_DIR"
NEXT_URL="https://api.bitbucket.org/2.0/repositories/${WORKSPACE}?pagelen=100"
while [ "$NEXT_URL" != "null" ] && [ -n "$NEXT_URL" ]; do
echo "Fetching: $NEXT_URL"
RESPONSE=$(curl -s -u "${USERNAME}:${APP_PASSWORD}" "$NEXT_URL")
echo "$RESPONSE" | jq -r '.values[].links.clone[] | select(.name=="https") | .href' \
| while read repo; do
REPO_NAME=$(echo "$repo" | sed 's/.*\///' | sed 's/\.git$//')
if [ -d "$REPO_NAME" ]; then
echo "Skipping $REPO_NAME (already exists)"
else
echo "Cloning $REPO_NAME..."
git clone "$repo"
fi
done
NEXT_URL=$(echo "$RESPONSE" | jq -r '.next // "null"')
done
echo "Done! All repositories cloned to $CLONE_DIR"Save this as clone-all.sh, make it executable (chmod +x clone-all.sh), and run it.
Method 2: Parallel Cloning (Fastest for 100+ Repos)
If you have GNU parallel installed, you can clone multiple repos simultaneously:
#!/bin/bash
USERNAME="your-username"
APP_PASSWORD="your-app-password"
WORKSPACE="your-workspace"
# Fetch all clone URLs
fetch_urls() {
NEXT_URL="https://api.bitbucket.org/2.0/repositories/${WORKSPACE}?pagelen=100"
while [ "$NEXT_URL" != "null" ] && [ -n "$NEXT_URL" ]; do
RESPONSE=$(curl -s -u "${USERNAME}:${APP_PASSWORD}" "$NEXT_URL")
echo "$RESPONSE" | jq -r '.values[].links.clone[] | select(.name=="https") | .href'
NEXT_URL=$(echo "$RESPONSE" | jq -r '.next // "null"')
done
}
mkdir -p bitbucket-repos && cd bitbucket-repos
# Clone 8 repos in parallel
fetch_urls | parallel -j 8 'git clone {}'This clones 8 repos simultaneously — a workspace with 100 repos finishes in ~2 minutes instead of 15+.
Install parallel if needed:
# Ubuntu/Debian
sudo apt install parallel
# macOS
brew install parallel
# RHEL/CentOS
sudo yum install parallelMethod 3: Python Script (Most Control)
For more control — filtering by project, skipping archived repos, or handling errors gracefully:
#!/usr/bin/env python3
"""Clone all repositories from a Bitbucket workspace."""
import os
import subprocess
import requests
USERNAME = "your-username"
APP_PASSWORD = "your-app-password"
WORKSPACE = "your-workspace"
CLONE_DIR = "./bitbucket-repos"
def get_all_repos():
"""Fetch all repository clone URLs from Bitbucket API."""
repos = []
url = f"https://api.bitbucket.org/2.0/repositories/{WORKSPACE}?pagelen=100"
while url:
response = requests.get(url, auth=(USERNAME, APP_PASSWORD))
response.raise_for_status()
data = response.json()
for repo in data.get("values", []):
name = repo["slug"]
clone_url = next(
(l["href"] for l in repo["links"]["clone"] if l["name"] == "https"),
None,
)
if clone_url:
repos.append({"name": name, "url": clone_url, "project": repo.get("project", {}).get("key", "NO_PROJECT")})
url = data.get("next")
return repos
def clone_repos(repos):
"""Clone all repositories into organized folders."""
os.makedirs(CLONE_DIR, exist_ok=True)
total = len(repos)
for i, repo in enumerate(repos, 1):
dest = os.path.join(CLONE_DIR, repo["name"])
if os.path.exists(dest):
print(f"[{i}/{total}] Skipping {repo['name']} (already exists)")
continue
print(f"[{i}/{total}] Cloning {repo['name']}...")
try:
subprocess.run(
["git", "clone", repo["url"], dest],
check=True,
capture_output=True,
)
except subprocess.CalledProcessError as e:
print(f" ERROR: Failed to clone {repo['name']}: {e.stderr.decode()}")
if __name__ == "__main__":
print(f"Fetching repositories from workspace: {WORKSPACE}")
repos = get_all_repos()
print(f"Found {len(repos)} repositories")
clone_repos(repos)
print("Done!")Filter by Project
Add this to clone only repos from a specific project:
# Filter repos by project key
repos = [r for r in get_all_repos() if r["project"] == "MYPROJECT"]Organize by Project Folder
# In clone_repos(), use project as subfolder:
dest = os.path.join(CLONE_DIR, repo["project"], repo["name"])Method 4: bucketcloner (Ready-Made Tool)
If you don't want to write scripts, use bucketcloner — a Python package built exactly for this:
# Install
pip install bucketcloner
# Clone all repos from all your workspaces
bucketcloner clone --user your-username --token your-app-password
# Clone repos from a specific workspace only
bucketcloner clone --user your-username --token your-app-password --workspace my-workspaceIt automatically:
- Handles pagination
- Creates
workspace/repositoryfolder structure - Skips already-cloned repos on re-run
- Supports both Bitbucket Cloud and Server
Method 5: Windows PowerShell
If you're on Windows without Bash:
$Username = "your-username"
$AppPassword = "your-app-password"
$Workspace = "your-workspace"
$CloneDir = ".\bitbucket-repos"
New-Item -ItemType Directory -Force -Path $CloneDir | Out-Null
Set-Location $CloneDir
$headers = @{
Authorization = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("${Username}:${AppPassword}"))
}
$url = "https://api.bitbucket.org/2.0/repositories/${Workspace}?pagelen=100"
while ($url -and $url -ne "null") {
$response = Invoke-RestMethod -Uri $url -Headers $headers
foreach ($repo in $response.values) {
$cloneUrl = ($repo.links.clone | Where-Object { $_.name -eq "https" }).href
$repoName = $repo.slug
if (Test-Path $repoName) {
Write-Host "Skipping $repoName (exists)" -ForegroundColor Yellow
} else {
Write-Host "Cloning $repoName..." -ForegroundColor Green
git clone $cloneUrl
}
}
$url = $response.next
}
Write-Host "Done! All repos cloned." -ForegroundColor CyanWhich Method Should You Use?
| Method | Best For | Speed | Setup |
|--------|----------|-------|-------|
| Bash one-liner | Quick one-time clone (under 100 repos) | Medium | None |
| Parallel cloning | Large workspaces (100+ repos) | Fastest | Install parallel |
| Python script | Filtering, error handling, automation | Medium | Install requests |
| bucketcloner | Non-developers, no scripting | Medium | pip install |
| PowerShell | Windows users | Medium | None |
Bonus: Migrate Bitbucket to GitHub
After cloning everything locally, push to GitHub:
#!/bin/bash
# Push all cloned Bitbucket repos to GitHub
GITHUB_ORG="your-github-org"
for dir in bitbucket-repos/*/; do
REPO_NAME=$(basename "$dir")
cd "$dir"
# Create repo on GitHub (requires gh CLI)
gh repo create "$GITHUB_ORG/$REPO_NAME" --private --source=. --push
cd ../..
echo "Migrated: $REPO_NAME"
doneCommon Issues
Authentication Failed
fatal: Authentication failed for 'https://bitbucket.org/...'
- Make sure you're using an App Password, not your account password
- Check that the App Password has Repositories → Read permission
- Verify your username is correct (not your email)
Rate Limiting
Bitbucket Cloud allows ~1,000 API requests per hour. For very large workspaces (500+ repos), add a delay:
# Add 1-second delay between clones
| while read repo; do git clone "$repo"; sleep 1; doneSSH Instead of HTTPS
Replace the jq filter to use SSH URLs:
| jq -r '.values[].links.clone[] | select(.name=="ssh") | .href'Make sure your SSH key is added to Bitbucket: Personal Settings → SSH Keys.