Automate GitHub topic updates! This tutorial shows how to efficiently bulk-update repository topics using cURL and Bash scripts, saving time and streamlining your workflow.
How to Bulk Update GitHub Repository Topics (Tags) Using cURL and Bash Scripts
GitHub repository topics (tags) are crucial for discoverability. They help developers find your projects when searching for specific technologies, frameworks, or use cases. However, GitHub's web interface only allows updating topics one repository at a time—which becomes tedious when managing multiple projects.
This guide shows you how to automate topic updates across multiple repositories using the GitHub API, cURL, and a simple Bash script. By the end, you'll be able to bulk-update topics in seconds.
Why Automate GitHub Topics?
Manual Updates (Slow):
- Go to each repo → Settings → Topics → Paste → Save
- 2-3 minutes per repository
- 5 repos = 15 minutes
- Error-prone (easy to miss repos)
Automated Updates (Fast):
- Write one script
- Run once
- 5 repos = 30 seconds
- Consistent topics across all repos
- Scriptable (repeat anytime)
Prerequisites
You'll need:
- GitHub Account (with repositories you own or manage)
- Terminal/Command Line (Mac, Linux, or Windows WSL)
- Personal Access Token (GitHub)
- Basic Bash knowledge (just copy-paste!)
All tools are free and built-in to most systems.
Step 1: Generate a GitHub Personal Access Token
Why You Need a Token
GitHub's API requires authentication. A Personal Access Token acts as a secure password for API requests.
How to Create One
-
Go to GitHub Settings:
- Visit: https://github.com/settings/tokens
- Or: GitHub → Settings (top-right) → Developer settings → Personal access tokens → Tokens (classic)
-
Click "Generate new token (classic)"
-
Fill in the form:
- Note: Name your token (e.g., github-topics-automation)
- Expiration: 90 days (or your preference)
- Select scopes: Check repo (full control of private repositories)
-
Click "Generate token"
-
Copy your token (starts with ghp_)
- ⚠️ Important: GitHub only shows it once. Copy and save it somewhere safe (or regenerate if lost).
- Never commit this token to Git!
- Treat it like a password.
Security Note
- Don't share your token publicly
- Don't hardcode it in Git repositories
- Use environment variables or stdin (like the script below)
- Rotate tokens periodically
Step 2: Understand the GitHub Topics API
The Endpoint
PUT https://api.github.com/repos/{OWNER}/{REPO}/topics
Parameters:
- {OWNER}: GitHub username or organization (e.g., octocat, facebook, OnDemandWorld)
- {REPO}: Repository name (e.g., Hello-World, react, erc1400-contracts)
Required Headers
Authorization: token YOUR_GITHUB_TOKEN
Accept: application/vnd.github.mercy-preview+json
Content-Type: application/json
Request Body Format
{
"names": [
"topic1",
"topic2",
"topic3"
]
}
Important: Use lowercase for topics (GitHub enforces this). Topics are limited to 50 per repository.
Step 3: Your First Manual cURL Command
Example Setup
Let's say you want to update topics for a repository:
- Owner: github-user (your GitHub username)
- Repository: my-awesome-project
- Topics: javascript, web-development, react, open-source
- Token: ghp_abc123xyz... (your Personal Access Token)
The cURL Command
Copy and paste this (replace the placeholder values):
curl -X PUT \
-H "Authorization: token ghp_abc123xyz..." \
-H "Accept: application/vnd.github.mercy-preview+json" \
-H "Content-Type: application/json" \
-d '{"names":["javascript","web-development","react","open-source"]}' \
https://api.github.com/repos/github-user/my-awesome-project/topics
Breaking It Down
curl -X PUT # HTTP method: PUT (update)
-H "Authorization: token YOUR_TOKEN" # Authentication header
-H "Accept: application/vnd.github.mercy-preview+json" # API version
-H "Content-Type: application/json" # Data format
-d '{"names":["topic1","topic2"]}' # Topics to add (JSON)
https://api.github.com/repos/OWNER/REPO/topics # GitHub API endpoint
Testing the Command
Replace these 3 things:
- ghp_abc123xyz... → Your actual token
- github-user → Your GitHub username
- my-awesome-project → Your repository name
Run it:
curl -X PUT \
-H "Authorization: token YOUR_TOKEN_HERE" \
-H "Accept: application/vnd.github.mercy-preview+json" \
-H "Content-Type: application/json" \
-d '{"names":["javascript","web-development","react","open-source"]}' \
https://api.github.com/repos/github-user/my-awesome-project/topics
Success Response
If it works, GitHub returns:
{
"names": ["javascript", "web-development", "react", "open-source"],
"sha": "abc123def456..."
}
Status code: 200 OK
Step 4: Update Multiple Repositories with a Bash Script
The Problem with Manual cURL
Running the command 5+ times is repetitive and error-prone. A Bash script automates this.
Full Bash Script
Save this as update-github-topics.sh:
{
#!/bin/bash
# GitHub Topics Automation Script
# Usage: ./update-github-topics.sh
# Prompts for token, then updates all repositories
# Color output for clarity
GREEN='\033[0;32m'
BLUE='\033[0;34m'
RED='\033[0;31m'
NC='\033[0m' # No Color
echo -e "${BLUE}📚 GitHub Topics Automation Script${NC}\n"
# Prompt for token (hidden input)
read -s -p "🔐 Enter your GitHub Personal Access Token (ghp_...): " TOKEN
echo ""
# Verify token is not empty
if [ -z "$TOKEN" ]; then
echo -e "${RED}❌ Error: Token cannot be empty${NC}"
exit 1
fi
echo -e "${BLUE}🚀 Starting topic updates...${NC}\n"
# Define repositories and their topics
# Format: "owner:repo:topic1,topic2,topic3"
declare -a REPOS=(
"github-user:my-awesome-project:javascript,web-development,react,open-source"
"github-user:data-processor:python,data-science,pandas,automation"
"github-user:api-wrapper:javascript,typescript,rest-api,nodejs"
"github-user:cli-tool:bash,command-line,devops,automation"
"github-user:mobile-app:react-native,mobile,ios,android"
)
# Counter for tracking success/failure
SUCCESS=0
FAILED=0
# Loop through each repository
for repo_info in "${REPOS[@]}"; do
# Parse the repo info
OWNER=$(echo $repo_info | cut -d: -f1)
REPO=$(echo $repo_info | cut -d: -f2)
TOPICS=$(echo $repo_info | cut -d: -f3)
# Convert comma-separated topics to JSON array format
# "topic1,topic2" → "\"topic1\",\"topic2\""
TOPICS_JSON=$(echo $TOPICS | sed 's/,/","/g' | sed 's/^/"/g' | sed 's/$/"/g')
echo -e "${BLUE}📝 Updating: ${OWNER}/${REPO}${NC}"
echo " Topics: $TOPICS"
# Make the API request
RESPONSE=$(curl -s -X PUT \
-H "Authorization: token $TOKEN" \
-H "Accept: application/vnd.github.mercy-preview+json" \
-H "Content-Type: application/json" \
-d "{\"names\":[${TOPICS_JSON}]}" \
https://api.github.com/repos/${OWNER}/${REPO}/topics \
-w "\n%{http_code}")
# Extract HTTP status code (last line of response)
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | head -n-1)
# Check if request was successful (HTTP 200)
if [ "$HTTP_CODE" = "200" ]; then
echo -e " ${GREEN}✅ Success${NC}\n"
((SUCCESS++))
else
echo -e " ${RED}❌ Failed (HTTP $HTTP_CODE)${NC}"
echo " Error: $BODY\n"
((FAILED++))
fi
done
# Summary
echo -e "${BLUE}═══════════════════════════════════════${NC}"
echo -e "${GREEN}✅ Successful: $SUCCESS${NC}"
echo -e "${RED}❌ Failed: $FAILED${NC}"
echo -e "${BLUE}═══════════════════════════════════════${NC}\n"
if [ $FAILED -eq 0 ]; then
echo -e "${GREEN}🎉 All topics updated successfully!${NC}"
exit 0
else
echo -e "${RED}⚠️ Some updates failed. Check errors above.${NC}"
exit 1
fi
}
How to Use This Script
Step 1: Create the file
# On your terminal, create the file
nano update-github-topics.sh
Step 2: Paste the script above
Step 3: Make it executable
chmod +x update-github-topics.sh
Step 4: Run it
./update-github-topics.sh
Step 5: Enter your token when prompted
📚 GitHub Topics Automation Script
🔐 Enter your GitHub Personal Access Token (ghp_...): ●●●●●●●●●●●●●●●
Expected Output
🚀 Starting topic updates...
📝 Updating: github-user/my-awesome-project
Topics: javascript,web-development,react,open-source
✅ Success
📝 Updating: github-user/data-processor
Topics: python,data-science,pandas,automation
✅ Success
📝 Updating: github-user/api-wrapper
Topics: javascript,typescript,rest-api,nodejs
✅ Success
📝 Updating: github-user/cli-tool
Topics: bash,command-line,devops,automation
✅ Success
📝 Updating: github-user/mobile-app
Topics: react-native,mobile,ios,android
✅ Success
═══════════════════════════════════════
✅ Successful: 5
❌ Failed: 0
═══════════════════════════════════════
🎉 All topics updated successfully!
Step 5: Customize the Script for Your Repositories
Edit the Repository List
Open update-github-topics.sh and find this section:
declare -a REPOS=(
"github-user:my-awesome-project:javascript,web-development,react,open-source"
"github-user:data-processor:python,data-science,pandas,automation"
"github-user:api-wrapper:javascript,typescript,rest-api,nodejs"
"github-user:cli-tool:bash,command-line,devops,automation"
"github-user:mobile-app:react-native,mobile,ios,android"
)
Change the Values
For each repository, follow this format:
"GITHUB_USERNAME:REPO_NAME:topic1,topic2,topic3,topic4"
Example 1: A Python project
"john-doe:machine-learning-toolkit:python,machine-learning,tensorflow,deep-learning"
Example 2: A React library
"company:react-components:react,javascript,ui-library,frontend"
Example 3: An organization repository
"facebook:react:javascript,react,frontend,facebook"
Add More Repositories
Just add more lines to the REPOS array:
declare -a REPOS=(
"user:repo1:topic1,topic2"
"user:repo2:topic3,topic4"
"user:repo3:topic5,topic6"
"org:repo4:topic7,topic8"
)
Step 6: Troubleshooting
Common Errors
Error: "Invalid request"
message: "Invalid request.\n\nFor 'links/0/schema'..."
Cause: JSON format is wrong.
Fix: Ensure topics are lowercase and in the correct JSON format: {"names":["topic1","topic2"]}
Error: "Problems parsing JSON"
message: "Problems parsing JSON"
Cause: Quotes or escaping issue.
Fix: Use single quotes for the entire JSON: '{"names":["topic1"]}'
Error: "Requires authentication"
message: "Requires authentication"
Cause: Token is invalid, expired, or missing.
Fix: Generate a new token at https://github.com/settings/tokens
Error: "Not Found"
message: "Not Found"
Cause: Repository owner or name is wrong.
Fix: Verify your GitHub username and repository names.
Test Your Token
Before running the full script, test your token:
curl -H "Authorization: token YOUR_TOKEN" \
https://api.github.com/user
If it works, you'll see your GitHub profile info. If it fails, your token is invalid.
Advanced: Real-World Example
Scenario: Open-Source Blockchain Project
Let's say you have 4 blockchain repositories and want to update topics with a consistent theme:
declare -a REPOS=(
"myorg:erc1400-contracts:solidity,smart-contracts,ethereum,erc-1400,security-token"
"myorg:sto-backend-api:typescript,express,rest-api,blockchain,backend"
"myorg:sto-web-client:react,next-js,web3,wallet-integration,frontend"
"myorg:erc1400-dashboard:react,next-js,dashboard,admin-panel,blockchain"
)
This ensures:
- Consistent branding across all repos
- Proper discoverability (developers searching "ethereum" find all 4)
- Easy maintenance (update once, applies everywhere)
- Version control (script is in Git, editable by team)
Best Practices
Topic Guidelines
- Use lowercase only: solidity ✅, Solidity ❌
- Use hyphens for multi-word: smart-contracts ✅, smart contracts ❌
- Be specific: machine-learning ✅, ml ❌
- Limit to 50 topics per repository (GitHub's hard limit)
- Aim for 5-15 topics per repo (sweet spot for discoverability)
Recommended Topics by Technology
Frontend: react, vue, angular, next-js, typescript, javascript, html-css
Backend: nodejs, express, python, django, rest-api, microservices, backend
Blockchain: ethereum, solidity, smart-contracts, web3, blockchain, erc-20, defi
DevOps: docker, kubernetes, ci-cd, devops, automation, bash, terraform
Data: python, data-science, machine-learning, pandas, numpy, tensorflow, analytics
Automate Further: GitHub Actions
If you want to update topics on every commit, use GitHub Actions:
Create .github/workflows/update-topics.yml:
name: Update GitHub Topics
on:
push:
branches: [main]
jobs:
update-topics:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Update Topics
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
./update-github-topics.sh
This runs your script automatically whenever you push to the main branch.
Verification
After running the script, verify topics were updated:
Method 1: Browser
- Go to: https://github.com/OWNER/REPO
- Scroll to About section
- Check Topics badge shows your topics
Method 2: API
curl -H "Accept: application/vnd.github+json" \
https://api.github.com/repos/OWNER/REPO | jq '.topics'
Output:
[
"javascript",
"react",
"web-development"
]
Method 3: GitHub's Topic Pages
Visit: https://github.com/topics/YOUR-TOPIC
Your repositories should appear in the list.
Conclusion
You now have a reusable, automated system to manage GitHub topics across multiple repositories. This approach:
✅ Saves time: Update 10 repos in 30 seconds
✅ Improves consistency: Same topics across all projects
✅ Increases discoverability: Better SEO on GitHub
✅ Scales easily: Add more repos to the script
✅ Version controlled: Keep the script in Git
Quick Reference
One-Time Manual cURL
curl -X PUT \
-H "Authorization: token YOUR_TOKEN" \
-H "Accept: application/vnd.github.mercy-preview+json" \
-H "Content-Type: application/json" \
-d '{"names":["topic1","topic2"]}' \
https://api.github.com/repos/OWNER/REPO/topics
Run the Automation Script
chmod +x update-github-topics.sh
./update-github-topics.sh
Get Your Token
https://github.com/settings/tokens → Generate new token (classic) → Select repo scope
Happy topic-updating! 🚀
Leave a Comment