# API dla agentów AI

Source: https://docs.askaniela.com/pl/user/agents/

Jak agent AI, skrypt albo narzędzie do automatyzacji korzysta z API AskAnieli krok po kroku: klucz, jedna strona,
audyt witryny, intencja wyszukiwania, koszty, błędy i JSON wyników.

Każdy przykład z tej strony przed publikacją uruchomiliśmy na naszym API stagingowym. Wersja Markdown tej strony:
`https://docs.askaniela.com/pl/user/agents.md`.

## Dla kogo {#dla-kogo}

- **Agenci AI z narzędziem HTTP** (Claude, ChatGPT, Codex, Cursor i podobne): daj agentowi tę stronę,
  [przykładowy prompt systemowy](#prompt-systemowy) i klucz w zmiennej środowiskowej.
- **Skrypty** (Python, Node, shell): skopiuj przykłady [curl](#jedna-strona) albo [Python](#python).
- **Narzędzia do automatyzacji** (n8n, Make, Zapier): węzeł HTTP Request z `Authorization: Bearer gsj_...`,
  węzeł Wait i pętla po polu statusu.
- **MCP**: serwer MCP AskAnieli jest planowany nad tym samym API, z tymi samymi kluczami i limitami. Do tego czasu
  podłącz API jako narzędzie HTTP albo wygeneruj narzędzia z `openapi.json` (zob. [Co czytają agenci](#llms-openapi)).

## Przed pierwszym wywołaniem {#start}

| Co | Wartość |
|---|---|
| Adres bazowy | `https://api.askaniela.com` |
| Nagłówek | `Authorization: Bearer gsj_...` w każdym żądaniu (bez klucza: `401 unauthorized`) |
| Treść | JSON, `Content-Type: application/json` |
| Klucz | klucz projektu z zakładki **Klucze API** projektu ([Klucze API](https://docs.askaniela.com/pl/user/api-keys.md)) |
| Zakresy | `judge`, `reports:read`, `jobs` (domyślne) wystarczą do wszystkiego na tej stronie |

Klucze utworzone przed zmianą domyślnych zakresów (październik 2026) mogą nie mieć `jobs`. Wtedy audyty witryny
zwracają `403 insufficient_scope` z `error.details.required_scope`; utwórz nowy klucz z domyślnymi zakresami.
Daj każdemu agentowi osobny klucz z datą wygaśnięcia, trzymaj go w zmiennej środowiskowej i nigdy nie wklejaj
go do promptu, logu ani czatu.

```bash
export ASKANIELA_URL=https://api.askaniela.com
export ASKANIELA_API_KEY=gsj_...
```

### Mapa zadań {#mapa-zadan}

| Zadanie | Wywołanie | Zakres | Odpowiedź | Płatne |
|---|---|---|---|---|
| Sprawdź jedną stronę | `POST /v1/judge` | `judge` | `200`, raport w treści (1-3 min) | tak |
| Sprawdź do 10 stron | `POST /v1/judge/batch` | `judge` | `200`, `results[]` | tak |
| Cena audytu witryny | `POST /v1/audits/site/preview`, potem `/estimate` | `jobs` | `200` | nie |
| Audyt witryny | `POST /v1/audits/site` | `jobs` | `202`, potem odpytywanie | tak |
| Stan audytu | `GET /v1/audits/{id}` | `reports:read` | `200` | nie |
| Wyniki audytu | `GET /v1/audits/{id}/export.json` | `reports:read` | `202` w przygotowaniu, potem `200` | nie |
| Plan poprawek | `GET /v1/audits/{id}/fix-plan?format=md` | `reports:read` | `202` w trakcie generowania, potem Markdown | nie |
| Intencja wyszukiwania | `POST /v1/intent` | `judge` | `202`, potem `GET /v1/intent/{id}` | tak |
| Saldo i limity | `GET /v1/usage` | dowolny | `200` | nie |
| Ceny | `GET /v1/tiers` | bez klucza | `200` | nie |

## 1. Sprawdź jedną stronę {#jedna-strona}

```bash
curl -s "$ASKANIELA_URL/v1/judge" \
  -H "Authorization: Bearer $ASKANIELA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/"}' \
  --max-time 300 -D headers.txt -o report.json
grep -i x-cost-usd headers.txt     # x-cost-usd: 0.0665
```

- Wyślij dokładnie jedno z: `url`, `html` albo `text`. Opcjonalnie: `modes` (`["spam"]`, `["quality"]`,
  `["helpful"]` albo kilka; mniej trybów = taniej), `rules` (id z `GET /v1/rules`), `render_js`, `explain` (dowody
  i poprawki dla trafień), `report_lang` (`en` albo `pl`), `project_id`.
- Wywołanie jest synchroniczne. Jedna strona z pełnym katalogiem trwała na stagingu około 2 minut; mniej `modes`
  = szybciej. Ustaw limit czasu odczytu klienta na co najmniej 300 sekund.
- Odpowiedź nie zawiera id raportu. Zapisany raport znajdziesz przez
  `GET /v1/reports?url=https://example.com/&limit=1` (`id` = `rep_…`), potem `GET /v1/reports/{id}/fix-plan?format=md`.
- Więcej niż jedna strona: `POST /v1/judge/batch` z `{"items": [{"url": …}, …]}` (do 10) albo audyt witryny.

## 2. Audyt witryny {#audyt}

Audyt witryny sprawdza próbę stron z sitemapy albo z Twojej listy URL-i. Działa w tle.

**Krok 1: podgląd** (bezpłatny). Znajduje sitemapę i grupuje URL-e we wzorce.

```bash
curl -s "$ASKANIELA_URL/v1/audits/site/preview" \
  -H "Authorization: Bearer $ASKANIELA_API_KEY" -H "Content-Type: application/json" \
  -d '{"site": "example.com"}'
# {"preview_id": "7963…", "total": 94, "patterns": [{"pattern": "/user/<slug>", "count": 11, …}], …}
```

**Krok 2: wycena** (bezpłatna, nic się nie uruchamia). Wyślij treść, którą chcesz zlecić, z `preview_id`.

```bash
curl -s "$ASKANIELA_URL/v1/audits/site/estimate" \
  -H "Authorization: Bearer $ASKANIELA_API_KEY" -H "Content-Type: application/json" \
  -d '{"site": "example.com", "preview_id": "7963…", "pages": 5,
       "scope": {"include_patterns": ["/user/<slug>"]}}'
# {"selected": 11, "pages": 5, "rules": 119, "cost_usd": 0.3325, "cost_per_page_usd": 0.0665, "seconds": 50, …}
```

**Krok 3: zapytaj człowieka.** Pokaż `cost_usd`, `pages` i czas, poczekaj na zgodę. Agent nigdy nie powinien
uruchamiać audytu sam z siebie.

**Krok 4: zlecenie** (płatne). Wyślij tę samą treść jeden raz.

```bash
curl -s "$ASKANIELA_URL/v1/audits/site" \
  -H "Authorization: Bearer $ASKANIELA_API_KEY" -H "Content-Type: application/json" \
  -d '{"site": "example.com", "preview_id": "7963…", "pages": 5,
       "scope": {"include_patterns": ["/user/<slug>"]}}'
# 202 {"audit_id": "aud_2fptzm5h3hiruh6c", "job_id": "job_e74cap43wdgzyszz", "pages": 5, "patterns": 1}
```

Nie powtarzaj tego POST po przekroczeniu czasu: najpierw poszukaj audytu w `GET /v1/audits?limit=5`
(zob. [Bezpieczne ponawianie](#ponawianie)).

**Krok 5: czekaj.** Odpytuj `GET /v1/audits/{audit_id}` co 15-30 sekund, aż `progress.status` będzie końcowy.
Ten endpoint nie wysyła `Retry-After`, więc odstęp dobierz sam; `eta_seconds` podaje przewidywany czas.

```bash
curl -s "$ASKANIELA_URL/v1/audits/aud_2fptzm5h3hiruh6c" -H "Authorization: Bearer $ASKANIELA_API_KEY"
# {"progress": {"status": "running", "total": 5, "done": 1, "failed": 0, …}, "eta_seconds": 38, …}
```

| `progress.status` | Końcowy | Co robić |
|---|---|---|
| `queued`, `running` | nie | odpytuj dalej |
| `pausing`, `paused`, `cancelling` | nie | ktoś wstrzymał albo zatrzymał audyt; `POST /v1/audits/{id}/resume` wznawia wstrzymany |
| `done` | tak | pobierz wyniki; `failed > 0` znaczy, że części stron nie udało się sprawdzić |
| `failed` | tak | audyt przerwany (`error`); nieocenione strony wracają do salda |
| `cancelled` | tak | zatrzymany na stałe; ocenione strony zachowują raporty |

Audyt 5 stron z przykładu trwał na stagingu około 6 minut. Te same 8 stanów i przejścia opisuje
[Stany zadań](https://docs.askaniela.com/pl/guide/webhooks.md#job-statuses).

Zamiast odpytywać, możesz dostać webhook. Audyt nie ma pola `webhook_url`; właściciel albo admin organizacji
raz zakłada [webhook organizacji](https://docs.askaniela.com/pl/guide/developer-portal.md#organization-webhooks), który dostaje
`job.finished` z `job_id` audytu. Agent, który ma tylko klucz, powinien odpytywać.

**Krok 6: wyniki.** `export.json` przygotowuje się w tle: pierwsze żądanie może zwrócić
`202 {"status": "pending", "retry_after": 5}` z nagłówkiem `Retry-After`. Odczekaj tyle sekund i powtórz GET.

```bash
curl -s "$ASKANIELA_URL/v1/audits/aud_2fptzm5h3hiruh6c/export.json" -H "Authorization: Bearer $ASKANIELA_API_KEY"
```

**Krok 7: plan poprawek** w Markdown, gotowy dla człowieka albo innego agenta. Przy pierwszym żądaniu to samo
`202` + `Retry-After`; kolejne czytają zapisany plan bez opłaty. Język planu: `report_lang` audytu, a bez niego
nagłówek `Accept-Language`.

```bash
curl -s "$ASKANIELA_URL/v1/audits/aud_2fptzm5h3hiruh6c/fix-plan?format=md" -H "Authorization: Bearer $ASKANIELA_API_KEY"
# # Fix plan: example.com
# Source: site audit aud_2fptzm5h3hiruh6c · Generated: 2026-10-20 20:12 UTC
```

Dostępne też: `export.zip` (plik Markdown na stronę plus `audit.json`), `export.csv`, `export.html`,
`export.pdf` i `fix-plan?format=json`.

## 3. Intencja wyszukiwania dla frazy {#intencja}

```bash
curl -s "$ASKANIELA_URL/v1/intent?wait=60" \
  -H "Authorization: Bearer $ASKANIELA_API_KEY" -H "Content-Type: application/json" \
  -d '{"query": "best running shoes", "market": "en-US"}'
# 202 {"id": "itr_…", "status": "running", "tier": "basic", "price_usd": 0.0015, "report": null, …}
```

- Odpowiedź to `202` ze `status` `queued`, `running`, `done` albo `failed`. `?wait=N` (do 60) czeka na wynik;
  jeśli nie jest gotowy, odpytuj `GET /v1/intent/{id}` co 15 sekund do `done` albo `failed`.
- `price_usd` to opłata za tę frazę. Jest niższa, gdy można użyć świeżych wyników wyszukiwania
  (`freshness: auto`, domyślnie); `fresh` zawsze pobiera nowe wyniki za pełną cenę.
- `tier`: `basic` (intencja i najlepsza forma strony), `premium` (Ani czyta też czołowe strony) albo `compare`
  (Twoja strona z `compare_url` obok nich). Ceny: `GET /v1/tiers` (`intent[]`).
- Wiele fraz: `POST /v1/intent/batch` (JSON `items`) albo `POST /v1/intent/batch/csv`.

## Koszty i limity {#koszty}

- **Cena przed zleceniem**: audyt witryny ma `POST /v1/audits/site/estimate`; ceny stron i intencji są w
  `GET /v1/tiers` i w [Saldo i cennik](https://docs.askaniela.com/pl/user/balance-pricing.md).
- **Koszt wywołania**: każda płatna odpowiedź ma nagłówek `X-Cost-USD` (np. `0.0665` za stronę z pełnym
  katalogiem, `0.0315` za sam spam). Dla audytów i zadań to kwota zarezerwowana na starcie; nieocenione strony
  wracają później.
- **Saldo**: `GET /v1/usage` pokazuje zużycie dnia i miesiąca, limit i saldo w USD.
- **Limit wyczerpany**: `429 quota_exceeded` z `details` (`limit_usd`, `used_usd`, `requested_usd`,
  `remaining_usd`). API nie używa `402`; brak salda to zawsze `429 quota_exceeded`. Zatrzymaj się i powiedz
  człowiekowi; ponawianie nic nie da.
- **Za dużo żądań**: `429 rate_limited` z `Retry-After` i nagłówkami `RateLimit-*`. Odczekaj i ponów.
- **Praca w toku**: naraz działają najwyżej 2 audyty albo zadania na organizację (`429 too_many_active_jobs`).
  Poczekaj, aż któreś się skończy.

## Błędy {#bledy}

Każdy błąd z `/v1/*` ma jeden kształt. Rozgałęziaj po `code`, nigdy po `message`.

```json
{"error": {"code": "unknown_rule", "message": "Unknown rules: NOPE",
           "details": {"rules": ["NOPE"]}, "request_id": "37da8d48f0e74f579f984e1e1cf215e1"}}
```

`message` jest po angielsku; z nagłówkiem `Accept-Language: pl` po polsku. `code` się nie zmienia.

| HTTP | `code` | Co robi agent |
|---|---|---|
| 400 | `unknown_rule`, `rule_disabled`, `invalid_url`, `invalid_webhook_url` | popraw wejście; nie ponawiaj bez zmian |
| 401 | `unauthorized`, `invalid_api_key`, `api_key_expired` | zatrzymaj się i poproś o ważny klucz |
| 403 | `insufficient_scope` | zatrzymaj się; człowiek tworzy klucz z `details.required_scope` |
| 403 | `ip_not_allowed`, `org_suspended`, `forbidden` | zatrzymaj się i powiedz człowiekowi |
| 404 | `report_not_found`, `audit_not_found`, `job_not_found`, `intent_not_found` | sprawdź id (`rep_`, `aud_`, `job_`, `itr_`) |
| 409 | `job_not_controllable`, `audit_running`, `upgrade_unavailable` | stan nie pozwala na tę akcję; odczytaj status |
| 413 | `batch_too_large`, `audit_too_large`, `item_too_large` | podziel pracę albo użyj audytu witryny zamiast wsadu |
| 422 | `validation_error` | popraw treść; `details[]` wymienia pola |
| 422 | `sitemap_not_found`, `empty_selection`, `preview_expired`, `invalid_scope` | zmień źródło albo zakres; zrób podgląd jeszcze raz |
| 429 | `rate_limited`, `too_many_active_jobs` | odczekaj `Retry-After` sekund i ponów |
| 429 | `quota_exceeded` | zatrzymaj się; limit albo saldo wyczerpane |
| 502 | `fetch_failed` | nie udało się pobrać strony; powiedz człowiekowi, nie ponawiaj w pętli |
| 502 | `inference_failed` | żadnej reguły nie oceniono, bez opłaty; ponów raz później |
| 503 | `spend_cap_reached`, `beta_budget_reached`, `queue_unavailable`, `unavailable` | odczekaj `Retry-After` (często 3600 s) |
| 503 | `fix_plan_unavailable`, `export_unavailable` | bez opłaty; ponów GET później |

`502` albo `504` **bez** treści JSON pochodzi z bramy, np. w trakcie wdrożenia. GET ponów po 30 sekundach.
POST nie ponawiaj w ciemno (następna sekcja). Każda odpowiedź ma `X-Request-ID`; podaj go przy zgłoszeniu
problemu. Wszystkie kody: [Błędy](https://docs.askaniela.com/pl/reference/errors.md).

## Bezpieczne ponawianie {#ponawianie}

API nie ma jeszcze `Idempotency-Key`, więc powtórzony POST to nowa praca i nowa opłata.

- **GET** można powtarzać zawsze.
- **`POST /v1/judge`**: powtórka sprawdza i nalicza stronę jeszcze raz. Po przekroczeniu czasu najpierw szukaj
  raportu w `GET /v1/reports?url=…&limit=1`.
- **`POST /v1/audits/site`, `POST /v1/jobs`**: po przekroczeniu czasu albo błędzie bramy sprawdź
  `GET /v1/audits?limit=5` albo `GET /v1/jobs?limit=5`. Audyt zwykle już tam jest.
- **`POST /v1/intent`**: sprawdź `GET /v1/intent?q=<fraza>&limit=5`. Fraza, która utknęła w `running`, zostaje po
  około 40 minutach zamknięta jako `failed` ze zwrotem; nie zlecaj jej ponownie wcześniej.
- Wysyłaj własny `X-Request-ID` (`[A-Za-z0-9._-]{1,128}`), żeby łączyć żądania z własnymi logami.

## Jak czytać wyniki {#wyniki}

### Jedna strona (`POST /v1/judge`, `GET /v1/reports/{id}`)

| Pole | Znaczenie |
|---|---|
| `violations[]` | reguły polityk spamu |
| `quality_signals[]` | reguły jakości strony i treści pomocnych |
| `positives[]`, `labels[]`, `choices[]` | sygnały pozytywne, etykiety kontekstu (np. temat YMYL) i skale ocen |
| `not_assessed[]` | polityki, których nie da się ocenić z jednej strony, z `reason` |
| `skipped[]` | id reguł pominiętych przy wybranych `modes` albo `rules` |
| `fetch`, `extraction_ok`, `truncated` | jak odczytano stronę; `extraction_ok: false` = mało treści głównej |

Każda reguła ma `rule_id`, `policy`, `layer`, `kind`, `ok` (`false` = reguły nie udało się ocenić, zob. `error`)
i `score_raw` od 0 do 1. **Siła sygnału = `round(score_raw * 100)`, od 0 do 100**: 80 i więcej to silny, 50-79
średni, poniżej 50 niski. Pokazuj ją jako „sygnał 87 na 100”. To nie jest procent, prawdopodobieństwo ani
decyzja Google. Przy `explain: true` trafienia dostają `evidence.quotes[]` (`text`, `verified`: kod znalazł cytat
na stronie) i `evidence.fix`. Trafienie z `unsupported: true` nie ma zweryfikowanego cytatu i wymaga sprawdzenia
przez człowieka. Pomijaj pola wewnętrzne `z`, `coverage`, `top1_other`, `probability`, `entropy`, `samples`,
`spread` i `prompt_sha`; mogą się zmienić.

### Audyt witryny (`GET /v1/audits/{id}/export.json`, `schema_version` 1.1)

Eksport zawiera tylko potwierdzone trafienia. Przykładowy kształt (jedno trafienie, skrócone):

```json
{
  "schema_version": "1.1",
  "audit": {"id": "aud_…", "status": "done", "pages_total": 5, "pages_checked": 5, "pages_failed": 0,
            "pages_with_findings": 1, "site_levels": {"spam": "high", "quality": "low", "helpful": "low"},
            "note": "1 of 5 pages (20%) have a confirmed spam finding. …"},
  "pages": [{
    "report_id": "rep_…", "url": "https://example.com/best-deals", "pattern": "/<slug>", "risk": "high",
    "findings": 1, "spam_level": "high", "quality_level": "low", "helpful_level": "na",
    "items": [{
      "category": "spam", "rule_id": "SP-AFF", "policy": "Thin affiliation", "title": "…", "level": "high",
      "signal": 91, "evidence": [{"text": "…cytat ze strony…", "verified": true}],
      "fix": "…co zmienić na tej stronie…", "fix_personal": true,
      "sources": [{"label": "…", "url": "https://developers.google.com/search/docs/essentials/spam-policies#thin-affiliate-pages"}]
    }]
  }]
}
```

- `site_levels` wynikają z udziału stron z potwierdzonym trafieniem: poniżej 2% niski, 2-10% średni, powyżej 10%
  wysoki.
- Poziomy strony: `high`, `medium`, `low`, `na` (kryteria nie mają zastosowania, np. strona użytkowa) albo
  `unknown`.
- `risk` to najwyższy potwierdzony poziom strony: `high`, `medium` albo `none`.
- `signal` jest już w skali 0-100. `fix_personal: true` znaczy, że poprawkę napisano dla tej strony; inaczej to
  ogólna rada reguły. `sources` to oficjalne wytyczne Google, na których opiera się reguła.

## Przykład w Pythonie {#python}

Wymaga `httpx` (`pip install httpx`). Sprawdza jedną stronę, wycenia audyt i pokazuje, gdzie go zlecić i śledzić.
Komentarze w kodzie są po angielsku, jak w wersji angielskiej strony.

```python
"""AskAniela API from Python (httpx): one page, a site audit, results and costs."""

import os
import time

import httpx

API = os.environ.get("ASKANIELA_URL", "https://api.askaniela.com")
KEY = os.environ["ASKANIELA_API_KEY"]
FINAL = {"done", "failed", "cancelled"}

client = httpx.Client(
    base_url=API,
    headers={"Authorization": f"Bearer {KEY}"},
    timeout=httpx.Timeout(15.0, read=300.0),  # one page takes up to a few minutes
)


class AnielaError(Exception):
    def __init__(self, status: int, code: str, message: str, retry_after: str | None, request_id: str | None):
        super().__init__(f"{status} {code}: {message} (request_id={request_id})")
        self.status, self.code, self.retry_after, self.request_id = status, code, retry_after, request_id


def call(method: str, path: str, **kw) -> httpx.Response:
    r = client.request(method, path, **kw)
    if r.status_code >= 400:
        is_json = r.headers.get("content-type", "").startswith("application/json")
        err = r.json().get("error", {}) if is_json else {}  # a 502/504 from a proxy has no JSON body
        raise AnielaError(r.status_code, err.get("code", f"http_{r.status_code}"), err.get("message", r.text[:200]),
                          r.headers.get("Retry-After"), err.get("request_id"))
    return r


def cost(r: httpx.Response) -> float:
    return float(r.headers.get("X-Cost-USD", "0"))


def wait_ready(path: str, params: dict | None = None, max_wait: int = 900) -> httpx.Response:
    """GET that may answer 202 + Retry-After while a file is prepared. Repeating a GET is safe."""
    deadline = time.monotonic() + max_wait
    while True:
        r = call("GET", path, params=params)
        if r.status_code != 202:
            return r
        if time.monotonic() > deadline:
            raise TimeoutError(path)
        time.sleep(int(r.headers.get("Retry-After", "10")))


def judge_page(url: str) -> tuple[dict, float]:
    r = call("POST", "/v1/judge", json={"url": url, "modes": ["spam"]})
    return r.json(), cost(r)


def estimate_site_audit(site: str, pages: int) -> dict:
    body = {"site": site, "pages": pages}
    preview = call("POST", "/v1/audits/site/preview", json=body).json()
    body["preview_id"] = preview["preview_id"]
    return {"body": body, "estimate": call("POST", "/v1/audits/site/estimate", json=body).json()}


def start_site_audit(body: dict) -> str:
    """Paid. Call once, after the user accepted the estimate. Never retry this POST blindly."""
    return call("POST", "/v1/audits/site", json=body).json()["audit_id"]


def follow_audit(audit_id: str, every: int = 20) -> tuple[dict, str]:
    while True:
        a = call("GET", f"/v1/audits/{audit_id}").json()
        p = a["progress"]
        print(f"{p['status']}: {p['done']}/{p['total']} pages, failed {p['failed']}")
        if p["status"] in FINAL:
            break
        time.sleep(every)
    export = wait_ready(f"/v1/audits/{audit_id}/export.json").json()
    plan = wait_ready(f"/v1/audits/{audit_id}/fix-plan", params={"format": "md"}).text
    return export, plan


def strongest(report: dict) -> list[tuple[str, int]]:
    """Spam and quality rules with signal strength 0-100 (score x 100), strongest first."""
    rows = [(r["rule_id"], round(r["score_raw"] * 100))
            for key in ("violations", "quality_signals") for r in report[key] if r.get("ok")]
    return sorted(rows, key=lambda x: -x[1])


if __name__ == "__main__":
    print(call("GET", "/v1/usage").json()["org"]["month"])
    report, usd = judge_page("https://example.com/")
    print("cost", usd, "top rules", strongest(report)[:3])
    plan = estimate_site_audit("example.com", pages=5)
    print("estimate", plan["estimate"]["cost_usd"], "USD,", plan["estimate"]["seconds"], "s")
    # audit_id = start_site_audit(plan["body"])  # only after the user said yes
    # export, fix_plan_md = follow_audit(audit_id)
```

## Przykładowy prompt systemowy {#prompt-systemowy}

Wklej do instrukcji agenta i dostosuj limity. Prompt jest po angielsku, bo modele i narzędzia najczęściej
pracują w tym języku; możesz go przetłumaczyć.

```text
You can use the AskAniela API at https://api.askaniela.com to check web pages against Google spam policies
and page-quality guidelines. The API key is in the environment variable ASKANIELA_API_KEY; send it as
"Authorization: Bearer <key>". Never print, log or repeat the key.

Rules:
1. Check only sites the user owns or is authorised to analyse.
2. Before any paid call, get the price: POST /v1/audits/site/estimate for site audits, GET /v1/tiers for single
   pages and search intent. Tell the user the price and wait for an explicit yes. Never start a site audit on
   your own. Single pages up to $0.10 in total per conversation do not need a separate yes.
3. Never repeat a POST that creates work (judge, audits/site, jobs, intent) without checking first whether the
   first one went through (GET /v1/reports?url=…, /v1/audits?limit=5, /v1/intent?q=…). A repeat is charged again.
4. Repeating GET requests is safe. On 202 wait for Retry-After seconds. Poll audit status every 20-30 seconds.
5. Branch on error.code, never on the message. Stop and ask the user on 401, 403, quota_exceeded and fetch_failed.
   Wait Retry-After on rate_limited, too_many_active_jobs and 503 codes.
6. Report results as signal strength from 0 to 100 (score_raw x 100, or "signal" in exports): "signal 87 of 100".
   Never present it as a percentage, a probability or a Google penalty. Say "may break the policy" or
   "possible quality issue", not "violates".
7. Quote evidence only when verified is true. Use the fix plan (GET /v1/audits/{id}/fix-plan?format=md) for
   recommendations instead of inventing your own.
8. After every paid call, read the X-Cost-USD header and keep a running total for the user.
Docs for agents: https://docs.askaniela.com/llms.txt and https://docs.askaniela.com/user/agents.md
```

## Co mówią agentom llms.txt i openapi.json {#llms-openapi}

| Adres | Co daje |
|---|---|
| `https://docs.askaniela.com/llms.txt` | spis wszystkich stron dokumentacji jako linki Markdown z jednozdaniowym opisem (format [llms.txt](https://llmstxt.org/)) |
| `https://docs.askaniela.com/llms-full.txt` | wszystkie strony dokumentacji w jednym pliku Markdown |
| `https://docs.askaniela.com/<strona>.md` | dowolna strona jako czysty Markdown, np. `https://docs.askaniela.com/pl/user/agents.md`; strony polskie pod `/pl/` |
| `https://api.askaniela.com/llms.txt` | krótki spis na hoście API: linki do dokumentacji i adres OpenAPI |
| `https://api.askaniela.com/openapi.json` | aktualny opis API w OpenAPI 3.1 |

Znane braki `openapi.json`, które uzupełnia ta strona:

- Nie deklaruje jeszcze klucza `Bearer gsj_...`; podaje cookie sesji i logowanie hasłem. Agenci i generatory
  narzędzi powinni je pominąć i wysyłać `Authorization: Bearer gsj_...`.
- Nie ma przykładów żądań, błędy opisuje tylko jako `4XX`/`5XX`, a nagłówków `X-Cost-USD` i `Retry-After` nie
  deklaruje. Korzystaj z tabeli [Błędy](#bledy) i kroków powyżej.
- Zawiera też operacje konta, sesji i administracji. Udostępniaj agentowi tylko operacje z
  [mapy zadań](#mapa-zadan).

Dalej: [Szybki start API](https://docs.askaniela.com/pl/user/api-quickstart.md), [Klucze API](https://docs.askaniela.com/pl/user/api-keys.md),
[Przewodnik po audytach witryny](https://docs.askaniela.com/pl/guide/site-audits.md), [Webhooki i zadania](https://docs.askaniela.com/pl/guide/webhooks.md),
[Błędy](https://docs.askaniela.com/pl/reference/errors.md).
