# semrush-bridge API: инструкция для агента

Базовый адрес: `https://semrush.patrik-dreams-42.duckdns.org`
Машинная схема: `https://semrush.patrik-dreams-42.duckdns.org/openapi.json`, человеческая: `https://semrush.patrik-dreams-42.duckdns.org/docs`.

API отдаёт живые замеры Semrush (Keyword Magic Tool и Bulk Keyword Overview) по любой
базе-гео. Задача, под которую он сделан: подбор кейвордов под search-арбитраж
(FB → Google, RSOC/AFS). Нужны кейворды, которые монетизируются и при этом не рвут
цепочку «креатив → лендинг → блок».

## 1. Авторизация

Каждый запрос идёт с токеном, который выдал владелец сервиса:

```
Authorization: Bearer sbk_xxxxxxxxxxxxxxxx
```

Можно и так: `X-API-Key: sbk_…`. Токен личный: к нему привязан владелец, и каждый вызов
пишется в его статистику. Не логируй токен и не вставляй его в URL.

Проверить токен: `GET /v1/me` → `{owner, label, token_prefix, daily_limit, usage:{today,total}}`.

## 2. Эндпоинты

### POST /v1/keyword-magic
Один кластер вокруг ядра: phrase match, как `mode=1` в Keyword Magic.

```json
{"q": "leilão de imóveis", "db": "br", "limit": 20, "fresh": false}
```

- `q`: ядро, 1–120 символов.
- `db`: база Semrush, она же гео (`br`, `pt`, `it`, `pl`, `us`, `uk`, `ae`, `bg`, `ro`…). Это НЕ язык.
- `limit`: 1–100 строк в каждом срезе, по умолчанию 20.
- `fresh`: `true`, чтобы не брать ответ из кэша (кэш живёт 72 ч).

Ответ (цифры для примера):
```json
{
  "q": "leilão de imóveis", "db": "br", "currency": "USD",
  "summary": {"all_keywords": 8306, "total_volume": 61290, "avg_kd": 38},
  "by_volume": [Row, …],
  "by_cpc":    [Row, …],
  "cached": false, "fetched_at": "2026-10-01T12:00:00Z"
}
```
`by_volume` даёт головы кластера (самые узнаваемые формулировки). `by_cpc` даёт денежный
потолок всего кластера одним запросом и закрывает вопрос «а может, дальше есть дорогие».

### POST /v1/bulk
До 100 фраз одним замером.

```json
{"keywords": ["leilao de imoveis", "consórcio imobiliário", "home equity"], "db": "br",
 "fresh": false, "fold_diacritics": true}
```

Ответ:
```json
{
  "db": "br", "currency": "USD",
  "rows": [Row, …],
  "missing": ["фразы, которых нет в выдаче"],
  "cached": false, "cached_count": 0, "fetched_at": "…",
  "pairs": [{"keyword": "consórcio imobiliário", "folded": "consorcio imobiliario",
             "volume": 4400, "folded_volume": 1900, "plan_volume": 4400}]
}
```
- Дубли во входе схлопываются (без учёта регистра).
- Кэш хранится по отдельным фразам: повторный bulk с пересечением снимает только новые фразы.
- `fold_diacritics: true` добавляет к каждой фразе с диакритикой её форму без диакритики
  и возвращает пары в `pairs` (подробнее в §4). Обе формы вместе тоже должны уложиться в 100.
- `missing`: фразы, которые Semrush не вернул. Пустая выдача ≠ «объём ноль». Перепроверь
  фразу отдельно или проверь сессию через `/v1/status`.

### Row: строка замера
```json
{"keyword": "home equity", "volume": 6600, "kd": 51, "cpc": 16.44, "com": 0.58,
 "intent": ["informational"], "results": 126, "updated": "1 month",
 "needs_refresh": false, "currency": "USD"}
```
- `volume`: запросов в месяц в этом гео.
- `cpc`: ставка рекламодателя в Google Ads, в валюте `currency`. Обычно USD, но IT Bulk
  отдаёт EUR. Это НЕ твой RPC.
- `com`: competitive density, 0–1, то есть глубина аукциона.
- `kd`: SEO-сложность. Для арбитража почти не важна.
- `intent`: подмножество `informational | navigational | commercial | transactional`.
- `needs_refresh: true` значит, что Semrush пометил строку «For metrics, refresh». У таких
  строк `volume/cpc/com` валидны (если не null), а `kd`, `intent` и `updated` отдаются как
  null/пусто: их посчитанными не выдавать. Рефреш через API не делается, он тратит лимиты аккаунта.

### GET /v1/status
`{session:{state, checked_at, detail}, browser:{running}, queue:{busy, waiting}}`.
`state`: `ok` / `login_required` / `load_error` / `unknown`. Вызов бесплатный и браузер не трогает.

### GET /v1/me
Владелец токена и расход, см. §1.

## 3. Ошибки

Формат всегда один: `{"error": {"code": "...", "message": "..."}}`.

| HTTP | code | Что делать |
|---|---|---|
| 401 | `unauthorized` | Токена нет, он неверный или отозван. Попроси у владельца новый. |
| 422 | `bad_request` | Поправь запрос по тексту `message`. |
| 429 | `rate_limited` | Дневной лимит токена (сутки UTC) исчерпан. Подожди или попроси лимит. |
| 503 | `login_required` | **Сессия Semrush слетела.** Цифр нет. Передай человеку, что владельцу сервиса нужно выполнить `./login.sh`. Сам не обходи и никаких цифр не выдумывай. |
| 502 | `page_load_failed` | Зеркало не отдало страницу. Повтори через 1–2 минуты (бывает DNS-флап). Это не приговор ключу. |
| 503 | `busy` | Очередь к браузеру длинная. Повтори позже. |
| 500 | `internal` | Повтори один раз, потом сообщи человеку. |

Ошибка ≠ данные. Никогда не превращай ошибку или пустой ответ в вывод «ключ мёртвый / объём 0».

## 4. Как правильно пользоваться (важно)

1. **Скорость.** Браузер на сервере один, запросы идут строго по очереди в человеческом
   темпе. Keyword Magic стоит ~15–25 с (две страницы: по объёму и по CPC), Bulk ~10–20 с.
   Таймаут HTTP-клиента ставь ≥ 180 с. Параллельные запросы не ускоряют, они просто
   встают в очередь. Лучше один bulk на 60 фраз, чем 60 одиночных вызовов.
2. **Кэш.** Повтор в течение 72 ч приходит мгновенно с `cached: true`. `fresh: true` ставь,
   только если данные нужны прямо сейчас: каждый живой замер тратит лимиты общего аккаунта.
3. **Диакритика — это разные ключи.** `leilao de imoveis` 18 100 против `leilão de imóveis`
   5 400. Бывает и наоборот: PT `simular crédito habitação` 22 200 против 4 400 транслитом.
   Всегда меряй обе формы (`fold_diacritics: true`) и бери максимум (`plan_volume`) как
   плановую цифру. Для лендинга и фида при этом нужна та форма, которую ты реально используешь.
4. **Смотри Com. раньше CPC.** Дорогие английские хвосты в неанглийских базах — мираж:
   BR «home equity loan» $48 при Com 0.06, рекламодателей в гео нет. Ниже ~0.6 стоит
   насторожиться.
5. **Phrase match.** `keyword-magic` всегда работает в `mode=1`. Broad тащит чужой интент
   (`tattoo removal` в broad даёт потолок $185 целиком из `laser hair removal`).
6. **Оценка под арбитраж:** `RPM = CTR блока × RPC`. CPC влияет только на RPC. Объём —
   прокси на CTR блока (узнаваемость формулировки), а не на трафик: трафик покупается в FB.
   Ориентир для денежного топика: CPC ≥ ~$2, Com ≥ 0.65, у головы кластера ≥ 500 запросов.
7. **Рабочий порядок:** (а) bulk буквального ядра креатива; (б) `keyword-magic` по ядру,
   чтобы увидеть потолок `by_cpc`; (в) смежные кластеры по шагу «что человек делает дальше»;
   (г) bulk финалистов с `fold_diacritics`, затем отбор по Com и CPC.

## 5. Примеры

```bash
export SB=sbk_…   # токен
curl -s https://semrush.patrik-dreams-42.duckdns.org/v1/me -H "Authorization: Bearer $SB"

curl -s -X POST https://semrush.patrik-dreams-42.duckdns.org/v1/keyword-magic -H "Authorization: Bearer $SB" \
  -H 'Content-Type: application/json' --max-time 240 \
  -d '{"q":"consórcio imobiliário","db":"br","limit":15}'

curl -s -X POST https://semrush.patrik-dreams-42.duckdns.org/v1/bulk -H "Authorization: Bearer $SB" \
  -H 'Content-Type: application/json' --max-time 240 \
  -d '{"db":"br","fold_diacritics":true,"keywords":["consórcio imobiliário","avaliação de imóveis","home equity"]}'
```

```python
import httpx
c = httpx.Client(base_url="https://semrush.patrik-dreams-42.duckdns.org", headers={"Authorization": f"Bearer {TOKEN}"}, timeout=240)
r = c.post("/v1/bulk", json={"db": "pt", "keywords": ["crédito habitação"], "fold_diacritics": True})
r.raise_for_status()
for row in r.json()["rows"]:
    print(row["keyword"], row["volume"], row["cpc"], row["com"])
```
