> ## Documentation Index
> Fetch the complete documentation index at: https://docs.messora.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Crawl

> Rastreie sites inteiros via BFS a partir de uma URL semente, até 50 páginas por job.

# Crawl

O endpoint `POST /crawl` inicia um crawl BFS (busca em largura) a partir de
uma URL semente. Ele descobre links internos recursivamente e extrai o
conteúdo em Markdown de cada página encontrada.

## Endpoint

```
POST /crawl
```

## Autenticação

Header `X-API-Key` obrigatório. Veja [Autenticação](/pt-BR/authentication).

## Parâmetros

<ParamField body="url" type="string" required>
  URL semente para iniciar o crawl. SSRF guard aplicado na seed e em cada link descoberto.
</ParamField>

<ParamField body="max_pages" type="integer" required>
  Número máximo de páginas a rastrear (1–50). Valor recomendado: 10.
  Sem valor default — omitir resulta em erro `422`.
</ParamField>

## Custo

**1 crédito por página bem-sucedida.**

<Note>
  A partir de 5 créditos estimados, o Playground solicita confirmação do usuário antes de iniciar.
  Páginas com falha (bloqueio, timeout) não consomem créditos.
</Note>

## Fluxo assíncrono

1. Envie `POST /crawl` → receba `202` com `job_id`
2. Poll via `GET /jobs/{job_id}` a cada 2s
3. Quando `status: "SUCCESS"`, os resultados estão no campo `results`
4. O campo `stopped_reason` pode indicar `max_pages` se o limite foi atingido

## Exemplo

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.messora.dev/crawl \
    -H "Content-Type: application/json" \
    -H "X-API-Key: YOUR_API_KEY" \
    -d '{
      "url": "https://example.com",
      "max_pages": 10
    }'
  ```

  ```python Python theme={null}
  import requests
  import time

  # 1. Inicia o crawl
  response = requests.post(
      "https://api.messora.dev/crawl",
      headers={
          "Content-Type": "application/json",
          "X-API-Key": "YOUR_API_KEY",
      },
      json={
          "url": "https://example.com",
          "max_pages": 10,
      },
  )

  job_id = response.json()["job_id"]
  print(f"Job criado: {job_id}")

  # 2. Poll até conclusão
  while True:
      status = requests.get(
          f"https://api.messora.dev/jobs/{job_id}",
          headers={"X-API-Key": "YOUR_API_KEY"},
      ).json()

      if status["status"] in ("SUCCESS", "FAILURE"):
          break

      # Progresso parcial
      if "meta" in status:
          print(f"Progresso: {status['meta']}")

      time.sleep(2)

  # 3. Resultados
  for page in status.get("results", []):
      print(f"{page['url']}: {page['scrape_status']}")
  ```

  ```javascript JavaScript theme={null}
  // 1. Inicia o crawl
  const res = await fetch("https://api.messora.dev/crawl", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-Key": "YOUR_API_KEY",
    },
    body: JSON.stringify({
      url: "https://example.com",
      max_pages: 10,
    }),
  });

  const { job_id } = await res.json();
  console.log(`Job criado: ${job_id}`);

  // 2. Poll até conclusão
  let status;
  do {
    await new Promise((r) => setTimeout(r, 2000));
    const poll = await fetch(
      `https://api.messora.dev/jobs/${job_id}`,
      { headers: { "X-API-Key": "YOUR_API_KEY" } }
    );
    status = await poll.json();
  } while (!["SUCCESS", "FAILURE"].includes(status.status));

  // 3. Resultados
  for (const page of status.results || []) {
    console.log(`${page.url}: ${page.scrape_status}`);
  }
  ```
</CodeGroup>

## Resposta de sucesso (via polling)

```json theme={null}
{
  "status": "SUCCESS",
  "job_id": "550e8400-e29b-41d4-a716-446655440003",
  "results": [
    {
      "url": "https://example.com",
      "scrape_status": "success",
      "markdown": "# Example Domain\n\nThis domain is for use in illustrative examples...",
      "credits_used": 1
    },
    {
      "url": "https://example.com/about",
      "scrape_status": "success",
      "markdown": "# About\n\nMore information...",
      "credits_used": 1
    }
  ],
  "credits_used": 2,
  "stopped_reason": null
}
```

## Limites

| Contexto                       | `max_pages` máximo |
| ------------------------------ | ------------------ |
| API pública (`X-API-Key`)      | 50                 |
| Playground (sessão via cookie) | 10                 |

## Erros

| HTTP Status | Descrição                                                                                |
| ----------- | ---------------------------------------------------------------------------------------- |
| `401`       | `X-API-Key` ausente                                                                      |
| `403`       | API key inválida ou revogada                                                             |
| `422`       | `max_pages` ausente, fora do intervalo 1–50, seed interna (SSRF) ou parâmetros inválidos |
| `429`       | Rate limit excedido (10 req/min)                                                         |
