skills/ahrefs-python/SKILL.md
Manages Ahrefs API usage in Python using `ahrefs-python` library. Use when working with SEO / marketing related tasks or with data including backlinks, keywords, domain ratings, organic traffic, site audits, rank tracking, brand monitoring, web analytics (page views, visitors, traffic sources, referrers, devices), and project management (keyword lists, competitors, custom prompts). Covers `ahrefs-python` usage including AhrefsClient / AsyncAhrefsClient, typed request/response models, error handling, and all API sections including free public endpoints that work without an API key. Trigger this skill whenever the user mentions Ahrefs, ahrefs-python, or any SEO data retrieval task in Python.
npx skillsauth add ahrefs/ahrefs-api-skills ahrefs-pythonInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The Ahrefs API provides programmatic access to Ahrefs SEO data. The official Python SDK (ahrefs-python) provides typed request and response models for all endpoints, auto-generated from the OpenAPI spec.
Key capabilities:
pip3 install git+https://github.com/ahrefs/ahrefs-python.git
Requires Python 3.11+. Dependencies: httpx, pydantic.
The SDK has 105 methods across 11 API sections. The built-in search tool is the fastest way to find the right method — it returns matching method signatures, parameters, and return types directly, so there's no need to scan through a large reference.
Python (preferred when already in a Python context):
from ahrefs.search import search_api_methods
# Returns formatted text with method signatures, parameters, and return types
print(search_api_methods("domain rating"))
# Filter by API section and limit results
print(search_api_methods("backlinks", section="site-explorer", limit=3))
CLI (preferred when exploring from the terminal):
# Ensure python3 points to the interpreter where ahrefs-python is installed:
# which python3
# python3 -c "import ahrefs"
python3 -m ahrefs.api_search "domain rating"
python3 -m ahrefs.api_search "backlinks" --section site-explorer --limit 3
python3 -m ahrefs.api_search "batch" --json
python3 -m ahrefs.api_search --sections # list all API sections
ahrefs-python SDK. DO NOT make raw httpx/requests calls to the Ahrefs API.YYYY-MM-DD format (e.g. "2025-01-15").select on list endpoints to request only the columns you need. List endpoints return all columns by default, which wastes API units and increases response size.with / async with) for client lifecycle management.AsyncAhrefsClient with asyncio.gather() when making multiple independent API calls — parallelism saves wall-clock time and API round-trips.AHREFS_API_KEY environment variable or your preferred secrets mechanism.import os
from ahrefs import AhrefsClient
with AhrefsClient(api_key=os.environ["AHREFS_API_KEY"]) as client:
data = client.site_explorer_domain_rating(target="ahrefs.com", date="2025-01-15")
print(data.domain_rating) # 91.0
print(data.ahrefs_rank) # 3
Public endpoints work without an API key — just omit the api_key parameter:
from ahrefs import AhrefsClient
with AhrefsClient() as client:
ips = client.public_crawler_ips()
ranges = client.public_crawler_ip_ranges()
Only public_* methods work without a key. All other methods require authentication. An authenticated client can also call public methods — there is no need for a separate client or raw HTTP calls.
import os
import ahrefs
with ahrefs.AhrefsClient(
api_key=os.environ["AHREFS_API_KEY"], # omit for public-only access
base_url="...", # override API base URL (default: https://api.ahrefs.com/v3)
timeout=30.0, # request timeout in seconds (default: 60)
max_retries=3, # retries on transient errors (default: 2)
) as client:
...
Async client:
import os
from ahrefs import AsyncAhrefsClient
async with AsyncAhrefsClient(api_key=os.environ["AHREFS_API_KEY"]) as client:
data = await client.site_explorer_domain_rating(target="ahrefs.com", date="2025-01-15")
For parallel calls, use asyncio.gather:
import asyncio
async with AsyncAhrefsClient(api_key=os.environ["AHREFS_API_KEY"]) as client:
dr_ahrefs, dr_moz = await asyncio.gather(
client.site_explorer_domain_rating(target="ahrefs.com", date="2025-01-15"),
client.site_explorer_domain_rating(target="moz.com", date="2025-01-15"),
)
Two calling styles -- both are equivalent:
# Keyword arguments (recommended)
data = client.site_explorer_domain_rating(target="ahrefs.com", date="2025-01-15")
# Request objects (full type safety)
from ahrefs.types import SiteExplorerDomainRatingRequest
request = SiteExplorerDomainRatingRequest(target="ahrefs.com", date="2025-01-15")
data = client.site_explorer_domain_rating(request)
Method names follow {api_section}_{endpoint}, e.g. site_explorer_organic_keywords, keywords_explorer_overview.
Methods return typed Data objects directly.
Scalar endpoints return a single data object (or None):
data = client.site_explorer_domain_rating(target="ahrefs.com", date="2025-01-15")
print(data.domain_rating)
List endpoints return a list of data objects. There is no pagination — set limit to the number of results you need. Use select to request only the columns you need:
items = client.site_explorer_organic_keywords(
target="ahrefs.com",
date="2025-01-15",
select="keyword,volume,best_position",
order_by="volume:desc",
limit=10,
)
for item in items:
print(item.keyword, item.volume, item.best_position)
import ahrefs
try:
data = client.site_explorer_domain_rating(target="example.com", date="2025-01-15")
except ahrefs.AuthenticationError: # 401
...
except ahrefs.RateLimitError as e: # 429 -- e.retry_after has the delay
...
except ahrefs.NotFoundError: # 404
...
except ahrefs.APIError as e: # other 4xx/5xx -- e.status_code, e.response_body
...
except ahrefs.APIConnectionError: # network / timeout
...
All exceptions inherit from ahrefs.AhrefsError.
When errors occur and the API key is valid, call subscription_info_limits_and_usage() to diagnose — it returns the subscription plan, key expiration date, and usage vs limits. For 403 errors specifically, also read e.response_body for the exact reason (units limit, export rows limit, domains-per-week limit, etc.).
Most list endpoints share these parameters:
| Parameter | Type | Description |
|-----------|------|-------------|
| target | str | Domain, URL, or path to analyze |
| date | str | Date in YYYY-MM-DD format |
| date_from / date_to | str | Date range for history endpoints |
| country | str | Two-letter country code (ISO 3166-1 alpha-2) |
| select | str | Comma-separated columns to return |
| where | str | Filter expression |
| order_by | str | Column and direction, e.g. "volume:desc" |
| limit | int | Max results to return |
Parameters typed as enums in the API reference (CountryEnum, VolumeModeEnum, etc.) accept plain strings — pass country="us" not CountryEnum("us").
The where parameter takes a JSON string. Use json.dumps() to build it:
import json
where = json.dumps({"field": "volume", "is": ["gte", 1000]})
items = client.site_explorer_organic_keywords(
target="ahrefs.com", date="2025-01-15",
select="keyword,volume", where=where,
)
For full filter syntax (boolean combinators, operators, nested fields), see references/filter-syntax.md.
Unless the user requests otherwise:
all_backlinks, anchors, pages_by_backlinks, refdomains): Use history="live" for current live data. The API defaults to all_time which includes lost backlinks.organic_keywords, top_pages, metrics, etc.): Use today's date for the date parameter.Use search_api_methods("query") or python3 -m ahrefs.api_search "query" to find methods by keyword. Search covers all 105 methods across 11 API sections and returns complete signatures, parameters, and response fields. Results with very large field lists (e.g. site_audit_page_explorer with 608 fields) are truncated at 9K chars — if you see ... [truncated], use select to request only the columns you need rather than relying on the full field list.
Web Analytics has 34 endpoints across many dimensions — not all method names match the dimension name directly (e.g. pages data uses web_analytics_top_pages, not web_analytics_pages). Always use search_api_methods("web analytics <topic>") to find the correct method.
These endpoints require a project_id (not a target domain). Use from_ and to for datetime ranges — from is a Python reserved word, so the SDK uses from_ with automatic serialization to the correct API name.
# Overall traffic stats (scalar endpoint)
stats = client.web_analytics_stats(project_id=123)
print(stats.pageviews, stats.visitors, stats.visits)
# Dimension breakdown (list endpoint — use select)
browsers = client.web_analytics_browsers(
project_id=123,
from_="2025-01-01T00:00:00Z",
to="2025-01-31T23:59:59Z",
select="browser,visitors",
limit=10,
)
# Time-series chart (list endpoint — requires granularity)
chart = client.web_analytics_chart(
project_id=123,
granularity="daily",
from_="2025-01-01T00:00:00Z",
to="2025-01-31T23:59:59Z",
)
Management endpoints handle Rank Tracker project configuration and Brand Radar prompt management — creating projects, managing keyword lists, adding competitors, and managing custom Brand Radar prompts. Unlike other API sections, this section includes endpoints that modify state via PUT/POST/PATCH/DELETE. None of these endpoints use select, where, order_by, or limit.
management_projects() to list all projects and find project_id values.management_locations() to get valid country codes, language codes, and location IDs before adding keywords.keyword_list_id is NOT discoverable via the API — the user must provide it from the Ahrefs web UI. Keyword list methods: management_keyword_list_keywords (list), management_set_keyword_list_keywords (add via PUT), management_keyword_list_keywords_delete (remove).report_id (from the Brand Radar report URL). Methods: management_brand_radar_prompts (list), management_create_brand_radar_prompts (create), management_brand_radar_prompts_delete (remove).management_project_keywords_delete, management_project_competitors_delete, management_keyword_list_keywords_delete, or management_brand_radar_prompts_delete.# List all Rank Tracker projects
projects = client.management_projects()
for p in projects:
print(p.project_id, p.project_name, p.url)
# Add keywords to a project (requires nested request types)
from ahrefs.types import ManagementSetProjectKeywordsLocation, ManagementSetProjectKeywordsKeyword
client.management_set_project_keywords(
project_id=12345,
locations=[ManagementSetProjectKeywordsLocation(
country="us",
language="en",
)],
keywords=[ManagementSetProjectKeywordsKeyword(keyword="ahrefs seo")],
)
# Add competitors to a project
from ahrefs.types import ManagementCreateProjectCompetitorsCompetitor
client.management_create_project_competitors(
project_id=12345,
competitors=[ManagementCreateProjectCompetitorsCompetitor(
url="moz.com",
mode="domain",
)],
)
# List Brand Radar custom prompts
prompts = client.management_brand_radar_prompts(report_id="abc123")
for p in prompts:
print(p.prompt, p.country, p.created_at)
# Create custom prompts for specific countries
client.management_create_brand_radar_prompts(
report_id="abc123",
countries=["us", "gb"],
prompts=["What is the best SEO tool?"],
)
tools
Use when work should span one or more detached tasks but still behave like one job with a single owner context. TaskFlow is the durable flow substrate under authoring layers like Lobster, ACPX, plugins, or plain code. Keep conditional logic in the caller; use TaskFlow for flow identity, child-task linkage, waiting state, revision-checked mutations, and user-facing emergence.
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------
tools
A CLI tool for making authenticated requests to the X (Twitter) API. Use this skill when you need to post tweets, reply, quote, search, read posts, manage followers, send DMs, upload media, or interact with any X API v2 endpoint.