If You Do It Every Day, AI Can Probably Do It For You

How to use OpenAI API to automate tasks that vary just slightly each time

12 min read

If You Do It Every Day, AI Can Probably Do It For You

You know that task you do every morning? The one that takes 10–15 minutes and involves the same basic steps, but with slightly different details each time?

That is exactly what the OpenAI API was built to handle.

Most people think automation only works for identical, scripted tasks. But modern AI can handle variation, the kind of work where the process is the same, but the content changes.


Getting Started: Your First API Call

from openai import OpenAI

# Create the OpenAI client
# It automatically reads OPENAI_API_KEY from environment variables
client = OpenAI()

# Make an API call to generate text using the Responses API
response = client.responses.create(
    model="gpt-4o-mini",  # The AI model to use
    input="Write a professional email declining a meeting request."
)

# Print the generated response
print(response.output_text)

This sends your prompt to OpenAI and returns the result. That is the foundation. Everything else builds from here.

Note: This uses the Responses API, which OpenAI now recommends for all new projects. It is simpler than the older Chat Completions API and offers better performance, lower costs, and built-in support for tools like web search. Always check the Models page for the latest available models.

Why Responses API?

The Responses API is OpenAI's evolution of their Chat Completions API. Here is why it matters for your automations:

  • Simpler syntax: Use input instead of constructing message arrays, and get results with output_text instead of navigating nested objects. Less code to write, fewer places for bugs to hide.
  • Better performance: The AI gives you smarter, more accurate answers, which is especially helpful when you are building automations that need to think through problems or write code.
  • Lower costs (often): Features like response caching or Flex processing can reduce costs for repeated or asynchronous workloads. Always verify against current pricing for your specific use case.
  • Built-in tools (model-dependent): Support for web search, file search, image generation, and code interpreter via the Responses API tools. Availability depends on which model you use.
  • Future-proof: All new OpenAI features will launch on the Responses API first, so you will not need to rewrite your code later.

If you see older tutorials using chat.completions.create(), they still work, but responses.create() is the modern approach.


Keep Your API Key Safe: Use Environment Variables

Never hardcode your API key in your scripts. If you accidentally commit it to GitHub or share your code, someone could use your key and rack up charges on your account.

Instead, store it in a .env file:

Step 1: Create a file named .env in your project directory:

OPENAI_API_KEY=sk-proj-your-actual-api-key-here

Step 2: Install python-dotenv:

pip install python-dotenv

Step 3: Load your key from the environment:

from openai import OpenAI
from dotenv import load_dotenv

# Load environment variables from .env file into the system environment
load_dotenv()

# Create OpenAI client - it automatically reads OPENAI_API_KEY from environment
client = OpenAI()

# Make the API call using the Responses API
response = client.responses.create(
    model="gpt-4o-mini",
    input="Write a professional email declining a meeting request."
)

# Print the result
print(response.output_text)

Step 4: Add .env to your .gitignore file:

.env

This ensures your API key never gets committed to version control. Your code stays shareable, and your key stays private.


The Anatomy of a Good Automation Prompt

from openai import OpenAI

client = OpenAI()

# Define a reusable prompt template with a placeholder for the sender name
prompt = '''You are a professional email writer.

Draft a polite decline for a meeting request from {sender_name}, a potential vendor.

Context:
- We are not currently accepting new vendor proposals
- Keep the tone friendly but firm
- Suggest they check back in Q2 next year

Format: Return only the email body, no subject line.
Length: Under 100 words.'''

# Fill in the placeholder with actual data
filled_prompt = prompt.format(sender_name="John from Acme Corp")

# Make the API call with the customized prompt
response = client.responses.create(
    model="gpt-4o-mini",
    input=filled_prompt
)

# Print the generated email
print(response.output_text)

The more specific you are, the more consistent your results.


Error Handling and Reliability

When you are automating tasks, things will go wrong. The API might be slow, your internet might hiccup, or you might hit rate limits. Your script needs to handle these gracefully.

Why retry with exponential backoff?

If the API fails, you do not want to retry immediately. That is like knocking on a door, getting no answer, then immediately knocking again. Instead:

  • First retry: Wait 1 second
  • Second retry: Wait 2 seconds
  • Third retry: Wait 4 seconds

Each retry waits longer, giving the system time to recover.

What is jitter and why do we need it?

Jitter means adding a small random delay (like 0.1–0.5 seconds). Here is why it matters:

Imagine the OpenAI API goes down at 2:00 PM. Without jitter:

  • 1,000 people's scripts all fail at the exact same moment
  • They all schedule their retry for exactly 2:01 PM
  • At 2:01 PM, 1,000 requests hit the server simultaneously
  • The server gets overwhelmed again (this is called a thundering herd)

With jitter:

  • Those 1,000 retries spread out: 2:01:00.1, 2:01:00.3, 2:01:00.7, etc.
  • The server can handle the gradual flow of requests
  • Your automation is more likely to succeed

Now here is how to implement it:

import time
import random
from openai import OpenAI
from openai import (
    APITimeoutError,
    APIConnectionError,
    RateLimitError,
    APIStatusError,
)

client = OpenAI()

def call_openai_with_retry(prompt, max_retries=3, model="gpt-4o-mini", timeout=30):
    """
    Call OpenAI API with automatic retry logic for transient failures.

    Args:
        prompt: The prompt to send to the API
        max_retries: Maximum number of retry attempts (default: 3)
        model: Model to use (default: "gpt-4o-mini")
        timeout: Request timeout in seconds (default: 30)

    Returns:
        The generated text response from the API
    """
    for attempt in range(max_retries):
        try:
            # Make the API call using the Responses API
            response = client.responses.create(
                model=model,
                input=prompt,
                timeout=timeout,
            )
            return response.output_text

        except (APITimeoutError, APIConnectionError) as e:
            # Network issues or timeouts - these are transient, so retry
            if attempt < max_retries - 1:
                # Wait 1s, 2s, 4s, etc. + random 0-0.5s (exponential backoff with jitter)
                sleep_time = (2 ** attempt) + random.uniform(0, 0.5)
                print(f"Connection issue. Retrying in {sleep_time:.1f}s...")
                time.sleep(sleep_time)
                continue
            # If we've exhausted retries, raise the exception
            raise

        except RateLimitError as e:
            # Hit rate limits - respect Retry-After header if present
            if attempt < max_retries - 1:
                # Check if API provided a retry-after time
                retry_after = getattr(e, "retry_after", None)
                if retry_after:
                    print(f"Rate limited. Retrying after {retry_after}s...")
                    time.sleep(retry_after)
                else:
                    # No retry-after header, use exponential backoff
                    sleep_time = (2 ** attempt) + random.uniform(0, 0.5)
                    print(f"Rate limited. Retrying in {sleep_time:.1f}s...")
                    time.sleep(sleep_time)
                continue
            raise

        except APIStatusError as e:
            # HTTP errors - 5xx are server errors (transient), 4xx are client errors (not transient)
            status_code = getattr(e, "status_code", 500)
            if status_code >= 500 and attempt < max_retries - 1:
                # Server error - retry with backoff
                sleep_time = (2 ** attempt) + random.uniform(0, 0.5)
                print(f"Server error {status_code}. Retrying in {sleep_time:.1f}s...")
                time.sleep(sleep_time)
                continue
            # Client error (4xx) or exhausted retries - raise
            raise

# Example usage
try:
    result = call_openai_with_retry("Write a professional email declining a meeting request.")
    print(result)
except Exception as e:
    print(f"Failed after retries: {e}")

This handles the most common API issues: timeouts, connection failures, rate limits, and server errors. The exponential backoff with jitter prevents overwhelming the API when it comes back online.


Stay Current: Check the Documentation Regularly

The AI industry moves incredibly fast. Features that did not exist last month might solve your current problem today. Bugs you are struggling with might have been fixed in the latest release.

Make it a habit to check the OpenAI API documentation and Anthropic documentation on a weekly or monthly basis.

You will discover:

  • New models with better capabilities or lower costs
  • Updated features that could improve your automations
  • Deprecation notices for methods you might be using
  • Best practices that have evolved since you first learned
  • Bug fixes for issues you thought were your fault

The documentation is not just for beginners. It is your competitive advantage in an industry where things change constantly.

Bookmark these:


Final Thought

The best automations are not the most complex. They are the ones that save you from doing the same thing over and over.

If you find yourself copying, pasting, and tweaking the same work every day, that is your signal.

Build a simple script. Add an OpenAI API call. Test it. Improve it.

That is vibecoding. Automate the repetitive. Focus on what actually matters.

Follow my X account to not miss new articles: @Vibecodeswork