Интеграция Ahrefs API для анализа ссылочного профиля сайта
Ahrefs — один из крупнейших индексов обратных ссылок. API даёт программный доступ к этому индексу: ссылочный профиль домена, анкорный текст, динамика роста, токсичные ссылки, страницы с наибольшим количеством входящих ссылок. Ручная работа в интерфейсе Ahrefs подходит для разового аудита; для регулярного мониторинга нужна автоматизация через API.
Доступ к API
Ahrefs API доступен на планах Advanced и Enterprise. Аутентификация — через Bearer-токен:
import requests
from typing import Literal
class AhrefsClient:
BASE_URL = 'https://api.ahrefs.com/v3'
def __init__(self, api_token: str):
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {api_token}',
'Accept': 'application/json',
})
def _get(self, endpoint: str, params: dict) -> dict:
resp = self.session.get(
f'{self.BASE_URL}/{endpoint}',
params=params,
timeout=30,
)
resp.raise_for_status()
return resp.json()
Получение ссылочного профиля домена
def get_domain_metrics(self, target: str) -> dict:
return self._get('site-explorer/domain-rating', {
'target': target,
'output': 'json',
})
def get_backlinks_stats(self, target: str) -> dict:
return self._get('site-explorer/metrics', {
'target': target,
'mode': 'domain',
'output': 'json',
})
def get_backlinks(
self,
target: str,
limit: int = 1000,
order_by: str = 'domain_rating_source:desc',
link_type: Literal['dofollow', 'nofollow', 'all'] = 'all',
) -> dict:
return self._get('site-explorer/backlinks', {
'target': target,
'mode': 'domain',
'limit': limit,
'order_by': order_by,
'where': f'is_nofollow={"1" if link_type == "nofollow" else "0"}' if link_type != 'all' else None,
'output': 'json',
})
Анализ анкорного текста
Распределение анкоров — важный сигнал здоровья ссылочного профиля. Слишком много точных коммерческих анкоров может указывать на манипуляцию:
def get_anchors(self, target: str, limit: int = 500) -> dict:
return self._get('site-explorer/anchors', {
'target': target,
'mode': 'domain',
'limit': limit,
'order_by': 'backlinks:desc',
'output': 'json',
})
def analyze_anchor_distribution(anchors_data: dict) -> dict:
anchors = anchors_data.get('anchors', [])
categories = {
'brand': 0,
'exact_match': 0,
'partial_match': 0,
'naked_url': 0,
'generic': 0,
'other': 0,
}
# Упрощённая классификация — адаптируется под конкретный бренд/сайт
brand_terms = ['example', 'example.com', 'example inc']
generic_terms = ['here', 'click here', 'this', 'website', 'link', 'read more']
target_keywords = ['buy shoes', 'cheap shoes', 'shoes online'] # ключевые слова сайта
total = sum(a.get('backlinks', 0) for a in anchors)
for anchor_item in anchors:
anchor = anchor_item.get('anchor', '').lower().strip()
count = anchor_item.get('backlinks', 0)
if any(b in anchor for b in brand_terms):
categories['brand'] += count
elif anchor in target_keywords:
categories['exact_match'] += count
elif any(k in anchor for k in target_keywords):
categories['partial_match'] += count
elif anchor.startswith('http') or anchor.startswith('www'):
categories['naked_url'] += count
elif anchor in generic_terms:
categories['generic'] += count
else:
categories['other'] += count
return {
k: {'count': v, 'pct': round(v / total * 100, 1) if total else 0}
for k, v in categories.items()
}
Поиск токсичных ссылок
Признаки токсичного донора: низкий DR, spam score, нерелевантная тематика, большое количество исходящих ссылок:
def find_toxic_backlinks(backlinks: list[dict], thresholds: dict = None) -> list[dict]:
if thresholds is None:
thresholds = {
'min_dr': 5, # DR ниже 5 — подозрительно
'max_linked_domains': 500, # слишком много исходящих ссылок
}
toxic = []
for link in backlinks:
issues = []
dr = link.get('domain_rating_source', 0)
linked_domains = link.get('linked_domains_source', 0)
is_nofollow = link.get('is_nofollow', False)
if dr < thresholds['min_dr']:
issues.append(f'low_dr:{dr}')
if linked_domains > thresholds['max_linked_domains']:
issues.append(f'link_farm:{linked_domains}_outbound')
if issues and not is_nofollow:
toxic.append({
'url_from': link.get('url_from'),
'url_to': link.get('url_to'),
'dr': dr,
'issues': issues,
})
return sorted(toxic, key=lambda x: x['dr'])
Динамика роста ссылочного профиля
Резкий рост ссылок — возможный сигнал манипуляции (или вирусного контента). Постепенное снижение — потеря позиций донорами:
def get_refdomains_history(self, target: str, date_from: str, date_to: str) -> dict:
return self._get('site-explorer/refdomains-history', {
'target': target,
'mode': 'domain',
'date_from': date_from,
'date_to': date_to,
'output': 'json',
})
def analyze_growth_pattern(history: list[dict]) -> dict:
if len(history) < 2:
return {}
counts = [h.get('refdomains', 0) for h in history]
deltas = [counts[i] - counts[i-1] for i in range(1, len(counts))]
avg_growth = sum(deltas) / len(deltas)
max_spike = max(deltas)
return {
'avg_monthly_growth': round(avg_growth, 1),
'max_spike': max_spike,
'spike_ratio': round(max_spike / avg_growth, 1) if avg_growth > 0 else None,
'trend': 'growing' if deltas[-1] > 0 else 'declining',
}
Анализ страниц по количеству входящих ссылок
def get_best_pages_by_links(self, target: str, limit: int = 100) -> list[dict]:
data = self._get('site-explorer/pages-by-links', {
'target': target,
'mode': 'domain',
'limit': limit,
'order_by': 'referring_domains:desc',
'output': 'json',
})
return data.get('pages', [])
Топ страниц по входящим ссылкам — отправная точка для стратегии внутренней перелинковки: сильные страницы должны передавать вес на коммерческие.
Регулярный мониторинг и хранение
CREATE TABLE ahrefs_domain_snapshots (
id SERIAL PRIMARY KEY,
domain TEXT NOT NULL,
snapshot_date DATE NOT NULL,
domain_rating NUMERIC(5,1),
ahrefs_rank INTEGER,
backlinks_total INTEGER,
referring_domains INTEGER,
dofollow_domains INTEGER,
organic_keywords INTEGER,
organic_traffic INTEGER,
UNIQUE(domain, snapshot_date)
);
CREATE TABLE ahrefs_new_lost_backlinks (
id SERIAL PRIMARY KEY,
domain TEXT NOT NULL,
detected_at DATE NOT NULL,
type VARCHAR(10) NOT NULL, -- 'new' или 'lost'
url_from TEXT,
url_to TEXT,
dr_source NUMERIC(5,1),
is_nofollow BOOLEAN
);
Еженедельный снапшот метрик домена позволяет отследить, как изменения на сайте (публикация нового контента, линкбилдинг) влияют на DR и количество реферирующих доменов.
Лимиты и стоимость
Ahrefs API тарифицируется в «единицах» — каждый запрос потребляет определённое количество в зависимости от объёма данных. На плане Advanced — 500 единиц в месяц (примерно 500 запросов базовых метрик или 50 полных выгрузок). Для регулярного мониторинга 10–20 доменов с недельными снапшотами обычно достаточно.
Сроки
Базовая интеграция с выгрузкой метрик домена и топ-100 бэклинков в БД — 2 рабочих дня. С анализом анкоров, поиском токсичных ссылок, динамикой роста и алертами — 4–5 дней. Дашборд сравнения с конкурентами — ещё 1–2 дня.







