Calling an AI Chat API From Python to Automate Text Processing

Calling an AI Chat API From Python to Automate Text Processing

Python is already the go-to language for automation, but add an AI chat API to the mix and something interesting happens. Your scripts stop being passive processors and start making judgments about text. Summarize this log. Write a description for that product. Flag the unusual entries. None of that requires machine learning expertise on your end. You just need to know how to call an endpoint, handle the JSON that comes back, and pipe the result into something useful. This tutorial shows you exactly how to build that pipeline from scratch.

API Essentials

Automating text processing with a Python API call comes down to three core skills.

  • Keeping credentials safe by loading API keys from environment variables, never from hardcoded strings inside your source file.
  • Structuring a request body correctly so the model returns exactly the format your downstream code expects.
  • Parsing the JSON response and routing the extracted text into a real task, such as writing to a file, inserting into a database, or triggering an alert.

Getting Your Python Environment Ready Before Writing a Single Line

Before any code runs, your environment needs two things: the requests library and a safe place to store your API key.

Create a virtual environment first. This keeps your project dependencies isolated from everything else on your system.

python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
pip install requests python-dotenv

Next, create a .env file in your project root. This is where your API key lives, and only there.

ANTHROPIC_API_KEY=your_key_here

Add .env to your .gitignore immediately. This single habit prevents the most common security mistake in scripted API projects. Environment variables keep credentials out of version control and out of application logs. If the key ever needs to rotate, you change it in one place, and nothing in your codebase breaks.

Loading the key in Python looks like this:

import os
from dotenv import load_dotenv

load_dotenv()
api_key = os.getenv("ANTHROPIC_API_KEY")

That is all the setup you need. The rest is just HTTP.

How the API Request Body Is Structured

AI chat APIs follow a consistent pattern. You send a POST request with a JSON body that contains a model identifier, a list of messages, and a few optional parameters like temperature or max tokens. The server processes the request and returns a JSON response containing the generated text.

A minimal request body looks like this:

{
  "model": "claude-haiku-4-5-20251001",
  "max_tokens": 512,
  "messages": [
    {
      "role": "user",
      "content": "Summarize the following log file:\n\n[your log content here]"
    }
  ]
}

The messages array is the heart of the request. Each entry has a role, either user or assistant, and a content string. For single-turn automation tasks, one user message containing your instruction and the raw text is all you need.

The max_tokens parameter caps the length of the response. For log summaries, 512 tokens is usually sufficient. For longer documents, push it higher. Setting this deliberately keeps costs predictable when your scripts are running at volume.

Writing the Python Function That Makes the API Call

Here is a clean, working function that sends text to the API and returns the response as a plain Python string:

import requests
import os
from dotenv import load_dotenv

load_dotenv()

API_KEY = os.getenv("ANTHROPIC_API_KEY")
API_URL = "https://api.anthropic.com/v1/messages"

def call_ai_api(prompt_text: str, max_tokens: int = 512) -> str:
    headers = {
        "x-api-key": API_KEY,
        "anthropic-version": "2023-06-01",
        "content-type": "application/json"
    }

    payload = {
        "model": "claude-haiku-4-5-20251001",
        "max_tokens": max_tokens,
        "messages": [
            {"role": "user", "content": prompt_text}
        ]
    }

    response = requests.post(API_URL, headers=headers, json=payload)
    response.raise_for_status()
    data = response.json()
    return data["content"][0]["text"]

For scripted, high-frequency automation tasks, Claude Haiku is the model to reach for. It processes requests at low latency and costs a fraction of what heavier models charge per token, which makes a noticeable difference when your script is running hundreds of calls a day against log files or a feed of scraped content.

Passing json=payload to requests.post() automatically serializes your dictionary and sets the correct content-type header. The raise_for_status() call converts any 4xx or 5xx response into a Python exception right away, so your error handling logic stays concentrated in one place rather than scattered across conditional checks.

Reading the JSON Response Without Guessing Its Shape

The response body has a predictable structure. Knowing which fields to target saves you from writing fragile parsing code that breaks the moment the API adds new fields in a future version.

Key Fields Returned in Every API Response

Field Type What It Contains
content Array One or more content blocks, each with a type and text key
content[0].text String The actual generated text your script needs to extract
usage.input_tokens Integer Tokens consumed by your prompt
usage.output_tokens Integer Tokens consumed by the model’s response
stop_reason String Why generation stopped: end_turn means complete, max_tokens means truncated

Keep an eye on stop_reason. If it comes back as max_tokens, the model hit your limit before finishing its response. That is a signal to either raise max_tokens or trim the input before sending it. Catching this in code prevents your downstream task from acting on an incomplete result.

The usage fields are genuinely useful for monitoring. Log them alongside each call and you get a clear, auditable record of how much each automation task is spending. That data helps you tune prompts to be more concise, which lowers costs without changing what the model produces.

Building a Log File Summarizer With These Pieces

Here is where everything connects. The goal is a script that reads a log file, breaks it into manageable chunks, sends each chunk to the API for summarization, and writes the collected output to a file. Follow these steps to build it:

  1. Read the log file into a Python string using open() in read mode, then call .strip() to remove trailing whitespace that would waste tokens without adding meaning.
  2. Chunk the content if it exceeds roughly 2,000 characters per call. Split on newline characters to keep individual log entries intact rather than cutting them mid-line.
  3. Build the prompt by prepending a clear instruction to each chunk. Something concise works best: “Summarize the key events, errors, and warnings in the following log excerpt in plain English.”
  4. Call the API for each chunk using your function, then collect the returned strings in a list so nothing gets dropped between iterations.
  5. Write the output to a new file or print it to stdout. If you processed multiple chunks, make one final API call asking the model to consolidate the list of chunk summaries into a single, clean executive summary.

Here is a minimal script that ties this together:

def summarize_log_file(file_path: str, output_path: str) -> None:
    with open(file_path, "r") as f:
        log_content = f.read().strip()

    chunk_size = 2000
    chunks = [log_content[i:i + chunk_size]
              for i in range(0, len(log_content), chunk_size)]

    summaries = []
    for i, chunk in enumerate(chunks):
        prompt = f"Summarize the key events and errors in this log excerpt:\n\n{chunk}"
        summary = call_ai_api(prompt)
        summaries.append(f"[Chunk {i + 1}]\n{summary}")
        print(f"Processed chunk {i + 1} of {len(chunks)}")

    final_text = "\n\n".join(summaries)

    with open(output_path, "w") as f:
        f.write(final_text)

    print(f"Summary written to {output_path}")

Run this against any server log, application error report, or deployment output and you get a readable summary in seconds instead of manually scanning thousands of lines for the one entry that matters.

Error Handling That Keeps Your Script Honest

API scripts running unattended need error handling that is honest about failures. If something breaks at 3 AM, you want to know about it, not find an empty output file with no explanation in the morning.

The most common failures are network timeouts, rate limit responses (HTTP 429), and KeyError exceptions when the response structure differs from what you expected. A retry wrapper covers the first two:

import time

def call_ai_api_with_retry(prompt_text: str, retries: int = 3) -> str:
    for attempt in range(retries):
        try:
            return call_ai_api(prompt_text)
        except requests.exceptions.HTTPError as e:
            if e.response.status_code == 429:
                wait = 2 ** attempt
                print(f"Rate limited. Waiting {wait}s before retry.")
                time.sleep(wait)
            else:
                raise
    raise RuntimeError("Max retries exceeded.")

Exponential backoff is the standard pattern for rate limits. Each failed attempt waits twice as long as the previous one, giving the API breathing room without hammering it with immediate retries. It is simple and it works reliably across most APIs that enforce rate limiting.

For unexpected response shapes, wrap your JSON field access in a try/except block and log the raw response body when something goes wrong. Seeing the actual response text is worth far more than a generic KeyError: 'content' message at 3 AM. The full exception hierarchy the library exposes is covered in depth in requests library documentation, and reading through it before deploying an overnight script is time well spent.

Adapting the Pattern for Scraped Content Descriptions

Log summarization is one application. The same script structure handles generating product descriptions from scraped page content, classifying support tickets by topic, extracting structured data from unstructured text, or tagging articles with categories. The core code does not change. Only the prompt and the expected output format do.

For product descriptions, the prompt might read: “Write a two-sentence product description for an e-commerce listing based on the following scraped content. Be factual and specific, avoid marketing language.” For ticket classification, try: “Classify this support ticket into exactly one of these categories: billing, technical, account, general. Return only the category name, nothing else.”

Being explicit about output format in the prompt saves a lot of post-processing work. If you ask for only a category name, you get exactly that string back, and your script can use it directly without any additional parsing. If you need JSON, say so in the prompt and validate the result with json.loads() before passing it anywhere downstream.

Token efficiency matters at scale too. A tight, specific instruction costs fewer input tokens than a long one that tries to cover every edge case. Spending five minutes trimming a prompt before deploying it at a thousand calls per day is a worthwhile trade.

From Raw Text to Processed Output, Running on Its Own

What you have built here is a general-purpose text processing pipeline. It takes raw input, sends it to an AI model with a clear instruction, and returns structured output your code can act on immediately. The pattern holds whether you are summarizing a hundred log files per day or processing a live feed of scraped articles every hour.

The real value comes from connecting this pipeline to other Python tools. Pipe the output into a SQLite database and query it for trends later. Feed summaries into a webhook to post alerts in a team channel. Schedule the script with cron to run every morning before anyone arrives at their desk. Each of these extensions is straightforward once the core API call is working reliably.

Start with one task and get it running cleanly. Monitor the token usage. Tighten the prompt. Add the retry logic. Then build outward from there. The API handles the language reasoning. Python handles everything else. That division of labor is exactly what makes this pattern worth knowing.

Leave a Reply

Your e-mail address will not be published.