DEV Community

Otieno Keith
Otieno Keith Subscriber

Posted on

Getting Started with API Automation: Simple Integration with Code

Intro – APIs + automation use cases

APIs make it easy for software to talk to software. With a few HTTP calls, you can move data between tools, trigger workflows, and generate reports without manual steps. Common automation use cases:

  • Lead capture → CRM: Send a form submission into HubSpot or Salesforce.
  • E‑commerce → accounting: Post new orders to your bookkeeping system.
  • App events → Slack/Email: Notify teams instantly when important events occur.
  • User actions → spreadsheets: Log signups or errors into Google Sheets for quick analytics.

You’ll build a small, practical pipeline: when a new user signs up, a Python script sends their data to Google Sheets via a webhook URL. Then you’ll optionally enrich those users with a second API and handle authentication securely.


Example Scenario: Send new user data to Google Sheets

Goal: Every time a new user signs up, append a row to a Google Sheet with fields like timestamp, email, plan, and source.

Approach:

  • Use a webhook endpoint provided by an automation tool (e.g., Zapier “Catch Hook” or Make/Integromat “Custom Webhook”).
  • Map the incoming JSON fields to columns in a “Signups” sheet.
  • POST JSON from Python to the webhook URL; the automation tool handles writing to Google Sheets.

Why a webhook instead of calling the Google Sheets API directly?

  • Faster to set up: no OAuth dance, scopes, or service accounts.
  • Easier to maintain: you can tweak the sheet mapping inside the automation UI.
  • Extensible: add more steps (Slack notice, CRM insert) without touching code.

Code Example: Python script using requests to POST data to a sheet via a webhook

Minimal snippet (the essence):

import requests
data = { "email": "jane@example.com", "plan": "pro" }
requests.post("https://hooks.zapier.com/... ", json=data)
Enter fullscreen mode Exit fullscreen mode

Full example with structure, validation, retry, and idempotency:

import os
import time
import uuid
import requests
from datetime import datetime
from typing import Dict, Any, Optional

WEBHOOK_URL = os.getenv("SHEETS_WEBHOOK_URL")  # e.g., the Zapier/Make webhook
DEFAULT_TIMEOUT_SEC = 5
MAX_RETRIES = 3
INITIAL_BACKOFF_SEC = 0.5

def generate_idempotency_key() -> str:
    return str(uuid.uuid4())

def post_with_retry(url: str, payload: Dict[str, Any], headers: Optional[Dict[str, str]] = None) -> requests.Response:
    attempt = 0
    backoff = INITIAL_BACKOFF_SEC
    last_exc = None

    while attempt < MAX_RETRIES:
        try:
            resp = requests.post(url, json=payload, headers=headers, timeout=DEFAULT_TIMEOUT_SEC)
            # Retry on transient server/network issues or rate limiting
            if resp.status_code in (429, 500, 502, 503, 504):
                time.sleep(backoff)
                backoff *= 2
                attempt += 1
                continue
            return resp
        except requests.RequestException as exc:
            last_exc = exc
            time.sleep(backoff)
            backoff *= 2
            attempt += 1

    if last_exc:
        raise last_exc
    raise RuntimeError("Exhausted retries posting to webhook")

def build_signup_payload(user: Dict[str, Any]) -> Dict[str, Any]:
    # Map your app’s fields to sheet columns
    return {
        "timestamp": datetime.utcnow().isoformat(),
        "email": user.get("email"),
        "name": user.get("name"),
        "plan": user.get("plan", "free"),
        "source": user.get("source", "website"),
        "utm_campaign": user.get("utm_campaign"),
        "metadata": user.get("metadata", {}),
    }

def send_signup_to_sheet(user: Dict[str, Any]) -> None:
    if not WEBHOOK_URL:
        raise EnvironmentError("SHEETS_WEBHOOK_URL not set")
    payload = build_signup_payload(user)
    headers = {
        "Content-Type": "application/json",
        # If your automation tool supports idempotency, pass a stable key to avoid duplicates
        "Idempotency-Key": generate_idempotency_key(),
        "User-Agent": "signup-automation/1.0"
    }
    resp = post_with_retry(WEBHOOK_URL, payload, headers=headers)
    if resp.status_code >= 400:
        raise RuntimeError(f"Webhook failed: {resp.status_code} {resp.text}")

if __name__ == "__main__":
    new_user = {
        "email": "jane@example.com",
        "name": "Jane Doe",
        "plan": "pro",
        "source": "landing-page",
        "utm_campaign": "spring-promo",
        "metadata": {"referrer": "twitter"}
    }
    send_signup_to_sheet(new_user)
    print("Signup posted to Google Sheets webhook.")
Enter fullscreen mode Exit fullscreen mode

How to wire the Google Sheets step:

  • In your automation tool, create a trigger step that receives a POST at a unique URL.
  • Add an action: “Create Spreadsheet Row” in Google Sheets.
  • Map JSON fields like email, plan, timestamp to columns.
  • Copy the webhook URL into SHEETS_WEBHOOK_URL.

Explanation of JSON, headers, HTTP methods

  • JSON: A text format for structured data.
    • Objects: { "email": "jane@example.com" }
    • Arrays: { "tags": ["beta", "newsletter"] }
    • Nested: { "user": { "email": "jane@example.com" } }
  • Headers: Metadata sent with requests.
    • Content-Type: application/json tells the server how to parse the body.
    • Authorization: Bearer <token> conveys credentials.
    • Idempotency-Key: <uuid> lets servers deduplicate repeated requests.
    • User-Agent: <string> identifies your client.
  • HTTP methods:
    • GET: retrieve data; should not change state.
    • POST: create/trigger actions; used for webhooks and inserts.
    • PUT/PATCH: update resources (full vs partial).
    • DELETE: remove resources.
  • Status codes:
    • 2xx success, 4xx client errors (bad input or auth), 5xx server errors (retry later).
    • 429 means rate limited; back off and retry.

Using a second API (optional chaining) and merging data

Let’s enrich the signup with a lightweight, no-auth API to estimate age from a first name using agify.io, then merge it into the payload before posting to the sheet.

import requests

def enrich_with_agify(user: Dict[str, Any]) -> Dict[str, Any]:
    enriched = dict(user)
    name = user.get("name")
    if not name:
        return enriched

    first_name = name.split()[0]
    try:
        resp = requests.get(
            "https://api.agify.io",
            params={"name": first_name},
            timeout=3
        )
        if resp.ok:
            guess = resp.json().get("age")
            enriched["age_guess"] = guess
    except requests.RequestException:
        # Non-fatal; keep original user data
        pass
    return enriched

def send_signup_to_sheet(user: Dict[str, Any]) -> None:
    if not WEBHOOK_URL:
        raise EnvironmentError("SHEETS_WEBHOOK_URL not set")

    # Optional chaining step:
    user = enrich_with_agify(user)

    payload = build_signup_payload(user)
    headers = {
        "Content-Type": "application/json",
        "Idempotency-Key": generate_idempotency_key(),
        "User-Agent": "signup-automation/1.0"
    }
    resp = post_with_retry(WEBHOOK_URL, payload, headers=headers)
    if resp.status_code >= 400:
        raise RuntimeError(f"Webhook failed: {resp.status_code} {resp.text}")
Enter fullscreen mode Exit fullscreen mode

Notes:

  • Keep enrichment best-effort. If it fails or times out, proceed without it.
  • Respect rate limits: add caching if you enrich lots of records with the same input.
  • If the second API requires auth, add headers per the next section.

Handling Authentication

Most production APIs require credentials. Common patterns:

  • API key in header: Authorization: Bearer <token> or X-API-Key: <key>.
  • OAuth2: Acquire an access token, then include it as a Bearer token.
  • Signed requests: HMAC signatures using a shared secret.

Best practices:

  • Store secrets in environment variables, not in code or git.
  • Rotate keys regularly and scope them with least privilege.
  • Use HTTPS only; never send tokens over plain HTTP.
  • Handle 401/403 specifically (refresh token or alert).

API key in header code snippet

import os
import requests

API_URL = "https://api.example.com/v1/data"
API_KEY = os.getenv("EXAMPLE_API_KEY")

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

payload = { "record_id": "123", "status": "active" }

resp = requests.post(API_URL, json=payload, headers=headers, timeout=5)
if resp.status_code == 401:
    raise RuntimeError("Unauthorized: check EXAMPLE_API_KEY")
resp.raise_for_status()
print(resp.json())
Enter fullscreen mode Exit fullscreen mode

Alternate header style used by some providers:

headers = {
    "X-API-Key": API_KEY,
    "Content-Type": "application/json"
}
Enter fullscreen mode Exit fullscreen mode

Testing the webhook end-to-end

Local smoke test for your webhook URL:

import requests
data = {
    "email": "jane@testco.com",
    "name": "Jane Doe",
    "plan": "pro",
    "source": "referral"
}
resp = requests.post(os.getenv("SHEETS_WEBHOOK_URL"), json=data, timeout=5)
print(resp.status_code, resp.text)
Enter fullscreen mode Exit fullscreen mode

If your webhook is private to your VPC, expose a controlled test endpoint or run the test in the same environment. In your automation tool’s UI, you can usually inspect recent deliveries, payloads, and errors.


Reliability: retries, idempotency, and validation

  • Retries: Implement exponential backoff on 429/5xx. Cap retries to avoid storms.
  • Idempotency: Generate a stable key for a given logical event (e.g., signup UUID). Many platforms deduplicate by Idempotency-Key.
  • Validation: Check emails are non-empty and sanity‑validate fields. Reject or log malformed events.
  • Observability: Log request IDs, keep a dead‑letter queue for failures, and add alerts on repeated failures.

Security basics for outbound calls

  • Never log raw secrets. Scrub headers before logging.
  • Use timeout on all HTTP calls to avoid hanging processes.
  • If chaining multiple APIs, be mindful of PII. Only send what’s necessary.
  • Respect rate limits and provider policies. If bulk jobs are needed, throttle or batch.

Summary of what automation accomplished

  • Automated data capture: Posted new signups directly to Google Sheets via a webhook no manual copy/paste.
  • Composable workflow: Inserted an optional enrichment step (second API) before writing to the sheet.
  • Production-friendly code: Added retries, timeouts, and idempotency to handle real-world failures.
  • Secure patterns: Demonstrated API key handling via headers and environment variables.

Outcome: You now have a template for API-powered automation collect data, optionally enrich it, and fan it out to destinations like spreadsheets, CRMs, or messaging tools with minimal code and strong reliability practices.

Top comments (0)