Skip to main content

Documentation Index

Fetch the complete documentation index at: https://acaas.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

This walkthrough builds a small but realistic Python program end-to-end. You take a plain-text changelog, generate a loud headline with /v1/scream, emphasize key words in each entry with /v1/emphasize, and write the result to a markdown file ready to paste into a release announcement. Along the way you handle authentication, rate limits, and errors the way you would in a real codebase.

What you will build

Given an input file CHANGELOG.txt:
the dashboard now loads twice as fast
fixed a bug where exports occasionally truncated the last column
added support for single sign-on via google workspace
The tool produces RELEASE_NOTES.md:
# RELEASE NOTES — APRIL 2026!!!

- the **dashboard** now loads **twice as fast**
- fixed a **bug** where exports occasionally **truncated** the last column
- added support for **single sign-on** via **google workspace**

Prerequisites

  • Python 3.10 or newer.
  • pip install requests.
  • An ACAAS API key. For the demo, any non-empty string works.
Set the key in your environment so it never lands in source control:
export ACAAS_API_KEY="demo"

Step 1: A typed client wrapper

Start with a thin Client class. It centralizes the base URL, the auth header, and the retry logic so the rest of the program reads like business logic.
client.py
import os
import time
from dataclasses import dataclass

import requests

BASE_URL = "https://api.acaas.example.com"


class ACAASError(Exception):
    """Raised when ACAAS returns an unrecoverable error."""


@dataclass
class Client:
    api_key: str
    session: requests.Session

    @classmethod
    def from_env(cls) -> "Client":
        api_key = os.environ.get("ACAAS_API_KEY")
        if not api_key:
            raise ACAASError("ACAAS_API_KEY is not set")
        session = requests.Session()
        session.headers["X-API-Key"] = api_key
        session.headers["Content-Type"] = "application/json"
        return cls(api_key=api_key, session=session)

    def _post(self, path: str, body: dict) -> dict:
        for attempt in range(5):
            resp = self.session.post(f"{BASE_URL}{path}", json=body)

            if resp.status_code == 200:
                return resp.json()

            if resp.status_code == 429:
                status = self.session.get(f"{BASE_URL}/v1/rate-limits").json()
                time.sleep(status["resets_in_seconds"])
                continue

            if resp.status_code in (401, 413, 422):
                raise ACAASError(f"{resp.status_code}: {resp.json().get('detail')}")

            time.sleep(2 ** attempt)  # 1s, 2s, 4s, 8s, 16s

        raise ACAASError("ACAAS unavailable after retries")
The retry loop covers the three failure shapes you actually see: rate limits (wait for the window), client errors (don’t retry — fix the input), and transient 5xx (exponential backoff).

Step 2: Endpoint methods

Add one method per endpoint you need. Each is a one-liner over _post.
client.py (continued)
    def scream(self, text: str, *, intensity: int = 3) -> str:
        data = self._post("/v1/scream", {"text": text, "intensity": intensity})
        return data["result"]

    def emphasize(self, text: str, *, ratio: float = 0.3) -> str:
        data = self._post("/v1/emphasize", {"text": text, "emphasis_ratio": ratio})
        return data["result"]
Both endpoints return the same envelope, but you only need the result field for this tool. If you wanted to log which words were emphasized, return data instead and let the caller pick fields.

Step 3: The amplifier

With the client in place, the program logic is short.
amplify.py
from datetime import date
from pathlib import Path

from client import Client


def amplify(changelog_path: Path, output_path: Path, *, month: str) -> None:
    client = Client.from_env()

    raw_entries = [
        line.strip()
        for line in changelog_path.read_text().splitlines()
        if line.strip()
    ]

    headline = client.scream(f"release notes — {month}", intensity=3)
    bulleted = [f"- {client.emphasize(entry, ratio=0.4)}" for entry in raw_entries]

    output_path.write_text(
        f"# {headline}\n\n" + "\n".join(bulleted) + "\n",
        encoding="utf-8",
    )


if __name__ == "__main__":
    today = date.today().strftime("%B %Y").lower()
    amplify(
        changelog_path=Path("CHANGELOG.txt"),
        output_path=Path("RELEASE_NOTES.md"),
        month=today,
    )
Run it:
python amplify.py

Step 4: Pacing for larger changelogs

The version above issues one request per entry, plus one for the headline. For a fifty-line changelog that fits comfortably inside the 100-request demo window. For a five-hundred-line changelog it does not. Add proactive pacing inspired by the rate limiting guide:
client.py (additional method)
    def pace_if_needed(self) -> None:
        status = self.session.get(f"{BASE_URL}/v1/rate-limits").json()

        if status["status"] == "at_limit":
            time.sleep(status["resets_in_seconds"])
        elif status["status"] == "approaching_limit":
            remaining = max(status["requests_remaining"], 1)
            time.sleep(status["resets_in_seconds"] / remaining)
Then call it inside the loop:
amplify.py (modified)
    bulleted = []
    for entry in raw_entries:
        client.pace_if_needed()
        bulleted.append(f"- {client.emphasize(entry, ratio=0.4)}")
Now the tool slows itself down before it spills 429s into your logs.

Step 5: Smoke-test before shipping

A short test confirms each endpoint is reachable and your key works.
smoke.py
from client import Client

client = Client.from_env()

assert "HELLO" in client.scream("hello"), "scream did not amplify"
assert "**" in client.emphasize("this is a test of emphasis"), "emphasize did not bold"

print("OK — ACAAS is reachable and the key is valid.")
Run it once before integrating into your release pipeline.

What you learned

  • One client, one session. Reusing a requests.Session reuses TCP connections and centralizes headers.
  • Retry the recoverable, raise the rest. 429 and 5xx are worth retrying; 401, 413, and 422 are not.
  • Pace proactively when it matters. For one-off scripts, 429 retries are fine. For batches, check status first and stretch the tail of the window evenly.
  • Compose endpoints. A useful tool rarely calls one endpoint — /v1/scream for the headline, /v1/emphasize for the body, and they speak the same envelope shape.

Next steps

Errors reference

Every status code, with copy-paste handler patterns.

Rate limiting

Quota mechanics, the status ladder, and proactive pacing.

Experimental emphasis

The endpoint that picks emphasis for you. Tune emphasis_ratio.

Full API reference

Every endpoint, request, and response, in one place.