DEV Community

Aaron VanSledright
Aaron VanSledright

Posted on • Originally published at seoscoreapi.com

Python SDK 1.3: Historical Trends, URL Comparisons, and Slack Alerts in Three Lines Each

The Python client for SEO Score API hit 1.3.0 today. It picks up everything we shipped through the latest sprint: side-by-side URL comparison, the /history and /history/domains endpoints, and webhook alerts (Slack-formatted by default) on the monitor system.

If you already have seoscoreapi installed, upgrade with:

pip install --upgrade seoscoreapi
Enter fullscreen mode Exit fullscreen mode

Then the rest of this post is a tour of what the new functions actually do.

What's in 1.3

Function Endpoint Tier
compare(urls, api_key) POST /compare Basic+
history(url, api_key, limit=100, since=None) GET /history Starter+
history_domains(api_key) GET /history/domains Starter+
add_monitor(..., webhook_url, alert_threshold) POST /monitors Paid

competitive_audit() and report_url() were already in 1.2 — they're listed here for completeness because they pair well with the new history functions.

1. Compare your page against competitors in one call

Before 1.3, doing a head-to-head comparison meant calling /audit for each URL and computing diffs yourself. Now compare() does it server-side and returns a structured diff block:

import os
from seoscoreapi import compare

result = compare(
    [
        "https://acme.com/pricing",
        "https://competitor-a.com/pricing",
        "https://competitor-b.com/pricing",
    ],
    api_key=os.environ["SEOSCORE_API_KEY"],
)

for url_data in result["urls"]:
    print(f"{url_data['url']}: {url_data['score']} ({url_data['grade']})")

print("\nGap analysis:")
for entry in result["diff"]["category_gaps"]:
    print(f"  {entry['category']}: leader is {entry['leader']} (+{entry['gap']:.1f})")
Enter fullscreen mode Exit fullscreen mode

compare() accepts 2–5 URLs in a single request and returns each URL's full audit, plus the diff object that tells you who is ahead in every category. It's Basic plan ($15/mo) and up.

This is the hook agency people have been asking for — drop it into a Streamlit or Slack-bot and you've got a "where do we stand against the top 3 competitors?" tool in 30 lines.

2. Pull the full audit history for any URL

If you've been auditing a URL on a paid plan, every audit's score breakdown is already in our database. history() returns the full timeseries plus a summary block:

from seoscoreapi import history

data = history("https://acme.com", api_key=os.environ["SEOSCORE_API_KEY"])

print(f"Tracked: {data['count']} audits")
print(f"  First: {data['summary']['first_score']}")
print(f"  Latest: {data['summary']['latest_score']}")
print(f"  Total delta: {data['summary']['total_delta']:+.1f}")
print(f"  Min/Max: {data['summary']['min_score']}/{data['summary']['max_score']}")

# Plot it
import matplotlib.pyplot as plt
ts = [p["timestamp"] for p in data["history"]]
scores = [p["score"] for p in data["history"]]
plt.plot(ts, scores)
plt.savefig("acme-trend.png")
Enter fullscreen mode Exit fullscreen mode

history() accepts limit and since parameters if you want to scope the response. The retention windows are tied to your tier:

Plan Retention
Starter ($5/mo) 30 days
Basic ($15/mo) 90 days
Pro ($39/mo) 1 year
Ultra ($99/mo) Unlimited

For a deeper write-up on what's in the history block, see the historical comparison post.

3. Get a one-shot view of every domain you track

history_domains() is the "all your stuff at a glance" call:

from seoscoreapi import history_domains

domains = history_domains(api_key=os.environ["SEOSCORE_API_KEY"])

# Sort by 30-day trend, worst first
domains.sort(key=lambda d: d.get("trend_30d", 0))

for d in domains[:10]:
    arrow = "" if d.get("trend_30d", 0) < 0 else ""
    print(f"{d['domain']:40} {d['latest_score']:>3} {arrow}{abs(d.get('trend_30d', 0)):.1f}")
Enter fullscreen mode Exit fullscreen mode

One call, one row per domain you've ever audited. Use it for an agency-wide "who needs attention this week?" dashboard or a Slack digest.

4. Get alerted in Slack when a score drops

add_monitor() now takes webhook_url and alert_threshold. If the webhook URL is a Slack incoming-webhook, the payload is auto-formatted as Block Kit:

from seoscoreapi import add_monitor

add_monitor(
    "https://acme.com",
    api_key=os.environ["SEOSCORE_API_KEY"],
    frequency="daily",
    webhook_url="https://hooks.slack.com/services/T0/B0/xxxx",
    alert_threshold=5,  # alert when the score drops 5+ points
)
Enter fullscreen mode Exit fullscreen mode

A 5-point drop on Acme's homepage now pings your #seo-alerts channel within minutes of the next scheduled audit run. Other https endpoints get the raw event JSON, so you can route alerts into PagerDuty, Discord, your own SIEM — anything that takes a webhook.

The alert_threshold is in score points, not percent. Five points is a reasonable starting value: small enough to catch real regressions, large enough to ignore the kind of noise that comes from a slow Core Web Vitals reading on a single audit.

A complete worked example: the 30-line agency monitor

Putting it together, here's the entire script we run for a small in-house team. It uses every new function in 1.3:

import os
from seoscoreapi import history_domains, history, compare

API_KEY = os.environ["SEOSCORE_API_KEY"]

# 1. Find URLs that dropped 2+ points this month
domains = history_domains(API_KEY)
dropped = [d for d in domains if d.get("trend_30d", 0) <= -2.0]
dropped.sort(key=lambda d: d["trend_30d"])

print(f"## {len(dropped)} URLs to investigate\n")

# 2. For each, pull the timeseries to see when the drop happened
for d in dropped[:5]:
    h = history(f"https://{d['domain']}", API_KEY, limit=20)
    points = h["history"][-5:]
    print(f"### {d['domain']}")
    for p in points:
        print(f"  {int(p['timestamp'])}: {p['score']}")

# 3. Compare your worst one against two competitors
worst = dropped[0]
result = compare(
    [
        f"https://{worst['domain']}",
        "https://competitor-a.com",
        "https://competitor-b.com",
    ],
    API_KEY,
)
for u in result["urls"]:
    print(f"\n{u['url']}: {u['score']} ({u['grade']})")
Enter fullscreen mode Exit fullscreen mode

That's a daily report that used to take a half-day in spreadsheets, in 30 lines, billable as a fixed-price retainer add-on. The full SDK reference is in the README and the auto-generated API docs.

Upgrading from 1.2

Nothing breaks. add_monitor() gets two new optional kwargs, but every existing call works as-is. The new functions (compare, history, history_domains) were either new in this release or added in 1.2 — if you've been on 1.2 already, this is a strict additive update.

pip install --upgrade seoscoreapi
Enter fullscreen mode Exit fullscreen mode

If you don't have a key yet, grab a free one or start on Starter for $5/mo to get historical tracking on day one.

Top comments (0)