DEV Community

Shivakumar
Shivakumar

Posted on

API for DevOps

From Zero to Automation: Understanding APIs and Scripting for DevOps

In the world of DevOps and software engineering, you hear the term API constantly. Whether you are connecting microservices, pulling data from the cloud, or automating a deployment pipeline, APIs are the glue that holds everything together.

But what exactly is an API, and how do we actually write code to interact with one?

In this guide, we will break down the theory using simple analogies and then build a real-world Python tool to automate a common DevOps task: finding stale branches on GitHub.


Part 1: What is an API?

API stands for Application Programming Interface. It is a set of rules that allows different software applications to talk to each other. It lets you use someone else's data or functionality without needing to know how their code is written.

The Best Analogy: The Restaurant

The easiest way to understand an API is to imagine a restaurant.

  • You are the Client (the app or user asking for data).
  • The Kitchen is the Server (the database or system that has the data).
  • The Waiter is the API.

You can’t just walk into the kitchen and start cooking. You need a messenger. You give your order (Request) to the waiter. The waiter takes it to the kitchen. The kitchen prepares the food. The waiter brings the food (Response) back to you.

You don't need to know how the stove works; you just need the waiter to handle the communication.


Part 2: The Interaction (Request & Response)

When you use an API, you are having a conversation. This is called an API Interaction. It always consists of two parts: the Request and the Response.

1. The Request (Asking)

This is the message you send to the API. It usually contains:

  • Endpoint: The URL (address) you are calling (e.g., api.github.com/users).
  • Method: The specific action you want to take.
  • GET: Retrieve data.
  • POST: Send/Create new data.
  • DELETE: Remove data.

  • Headers: Authentication keys (passwords) or file formats.

2. The Response (Answering)

The server replies with data (usually in JSON format) and a Status Code to tell you what happened:

  • 200 OK: Success.
  • 401 Unauthorized: You forgot your password/token.
  • 404 Not Found: The link is wrong.
  • 500 Error: The server crashed.

Part 3: Real-World DevOps Automation

Theory is great, but DevOps is about doing. Let's write a Python script using the GitHub API.

The Challenge:
Repositories often get cluttered with old feature branches that developers forgot to delete. We want a tool that finds "Stale Branches" (branches that haven't been updated in 90+ days).

The Requirements:
To make this tool production-ready, we must handle:

  1. Authentication: Using a Token so we don't get blocked.
  2. Pagination: GitHub only lists 30 branches at a time; we need all of them.
  3. Rate Limiting: We need to pause the script if we hit GitHub's request limit so it doesn't crash.

The Python Solution

Note: You will need the requests library (pip install requests) to run this.

import requests
import time
from datetime import datetime, timezone, timedelta

# --- CONFIGURATION ---
GITHUB_TOKEN = "YOUR_GITHUB_TOKEN_HERE" 
REPO_OWNER = "octocat"
REPO_NAME = "Hello-World"
STALE_DAYS = 90
API_URL = "https://api.github.com"

class GitHubAPI:
    def __init__(self, token):
        self.headers = {
            "Authorization": f"Bearer {token}",
            "Accept": "application/vnd.github.v3+json"
        }

    def _handle_rate_limit(self, response):
        """Checks rate limit headers and sleeps if exhausted."""
        remaining = int(response.headers.get("X-RateLimit-Remaining", 1))
        reset_time = int(response.headers.get("X-RateLimit-Reset", 0))

        if remaining < 2:
            sleep_time = reset_time - int(time.time()) + 1
            print(f"[!] Rate limit exhausted. Sleeping for {sleep_time} seconds...")
            time.sleep(sleep_time)

    def get(self, url, params=None):
        """Wrapper for requests.get with rate limit handling."""
        while True:
            response = requests.get(url, headers=self.headers, params=params)
            self._handle_rate_limit(response)

            if response.status_code == 200:
                return response.json()
            elif response.status_code == 403:
                # Retry if it was just a rate limit issue
                continue 
            else:
                print(f"Error {response.status_code}")
                return None

    def get_all_branches(self, owner, repo):
        """Paginates through all branches to get the full list."""
        branches = []
        page = 1
        while True:
            print(f"[*] Fetching branches page {page}...")
            data = self.get(f"{API_URL}/repos/{owner}/{repo}/branches", 
                            params={"per_page": 100, "page": page})
            if not data: break
            branches.extend(data)
            if len(data) < 100: break
            page += 1
        return branches

    def get_commit_date(self, url):
        """Fetches the commit detail to get the exact date."""
        data = self.get(url)
        return data["commit"]["committer"]["date"] if data else None

# --- MAIN EXECUTION ---
api = GitHubAPI(GITHUB_TOKEN)
branches = api.get_all_branches(REPO_OWNER, REPO_NAME)
limit_date = datetime.now(timezone.utc) - timedelta(days=STALE_DAYS)

print(f"\nChecking {len(branches)} branches for staleness...")

for branch in branches:
    date_str = api.get_commit_date(branch["commit"]["url"])
    if date_str:
        commit_date = datetime.fromisoformat(date_str.replace("Z", "+00:00"))
        if commit_date < limit_date:
            print(f"[STALE] {branch['name']} (Last updated: {commit_date.strftime('%Y-%m-%d')})")

print("Done!")

Enter fullscreen mode Exit fullscreen mode

Why This Code Works

  1. It loops through pages: We use a while loop to keep asking for "page 2", "page 3", etc., until GitHub runs out of branches.
  2. It respects limits: The _handle_rate_limit function looks at the headers GitHub sends back. If we are out of requests, the script automatically pauses (sleeps) until our quota resets.
  3. It handles dates: It compares the "Last Updated" date of every branch against our 90-day threshold to identify the ones that can be deleted.

Conclusion

Understanding APIs moves you from "clicking buttons" to "building systems." Whether you are fetching weather data or cleaning up a massive code repository, the concepts of Request, Response, and Pagination remain the same.

Try running the script above on your own repositories and see how many stale branches you can find!


Would you like me to create a "Thumbnail" or hero image description that you could use if you publish this blog post?

Top comments (0)