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

# API de Búsqueda

> Busca en la web con lenguaje natural y obtén enlaces estructurados — la API de búsqueda de Olostep.

El endpoint de Olostep `/v1/searches` te permite buscar en la web con una consulta en lenguaje natural y obtener una lista deduplicada de enlaces relevantes con títulos y descripciones.

* Envía una consulta en inglés simple
* Recibe enlaces estructurados de toda la web
* Opcionalmente, raspa cada URL devuelta en un solo viaje de ida y vuelta e incrusta `markdown_content` / `html_content` directamente en la respuesta
* Filtra por dominio, controla el número de resultados y limita el tiempo de raspado

Buscará la consulta semánticamente en toda la web y devolverá resultados.

Para detalles de la API, consulta la [Referencia de la API del Endpoint de Búsqueda](/api-reference/searches/create).

## Instalación

<CodeGroup>
  ```python Python theme={null}
  pip install olostep
  ```

  ```javascript Node theme={null}
  npm install olostep
  ```

  ```bash cURL theme={null}
  # curl está disponible por defecto en macOS, Linux y Windows
  ```

  ```javascript Node (API) theme={null}
  npm install node-fetch
  ```

  ```bash Python (API) theme={null}
  pip install requests
  ```
</CodeGroup>

## Uso básico

Envía una consulta en lenguaje natural y recibe una lista de enlaces relevantes.

<CodeGroup>
  ```python Python theme={null}
  from olostep import Olostep

  client = Olostep(api_key="YOUR_REAL_KEY")

  search = client.searches.create("Best Answer Engine Optimization startups")

  print(search.id, len(search.links))
  ```

  ```js Node theme={null}
  import Olostep from 'olostep'

  const client = new Olostep({ apiKey: 'YOUR_REAL_KEY' })

  const search = await client.searches.create('Best Answer Engine Optimization startups')

  console.log(search.id, search.links.length)
  ```

  ```bash cURL theme={null}
  curl -s -X POST "https://api.olostep.com/v1/searches" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "Best Answer Engine Optimization startups"
    }'
  ```

  ```js Node (API) theme={null}
  const res = await fetch('https://api.olostep.com/v1/searches', {
    method: 'POST',
    headers: { 'Authorization': 'Bearer <YOUR_API_KEY>', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      query: 'Best Answer Engine Optimization startups'
    })
  })
  console.log(await res.json())
  ```

  ```python Python (API) theme={null}
  import requests, json

  endpoint = "https://api.olostep.com/v1/searches"
  payload = {
    "query": "Best Answer Engine Optimization startups"
  }
  headers = {"Authorization": "Bearer <YOUR_API_KEY>", "Content-Type": "application/json"}

  response = requests.post(endpoint, json=payload, headers=headers)
  print(json.dumps(response.json(), indent=2))
  ```
</CodeGroup>

## Parámetros de la solicitud

| Campo             | Tipo      | Requerido | Predeterminado | Descripción                                                                                                                                                          |
| ----------------- | --------- | --------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query`           | string    | sí        | —              | La consulta de búsqueda en lenguaje natural.                                                                                                                         |
| `limit`           | integer   | no        | `12`           | Número máximo de enlaces a devolver después de la deduplicación. Debe estar entre `1` y `25`.                                                                        |
| `include_domains` | string\[] | no        | `[]`           | Restringe los resultados a estos dominios. Solo hosts básicos — los prefijos `http(s)://` y las barras finales se eliminan automáticamente.                          |
| `exclude_domains` | string\[] | no        | `[]`           | Excluye resultados de estos dominios. Solo hosts básicos — los prefijos `http(s)://` y las barras finales se eliminan automáticamente.                               |
| `scrape_options`  | object    | no        | —              | Cuando se proporciona, cada enlace devuelto también se raspa y su contenido se incrusta en la respuesta. Consulta [scrape\_options](#scrape-options) a continuación. |

### Limitando el número de resultados

```json theme={null}
{
  "query": "¿Qué está pasando con el cierre de Sora de OpenAI?",
  "limit": 5
}
```

### Filtrando por dominio

`include_domains` reduce los resultados a una lista blanca; `exclude_domains` filtra las fuentes no deseadas. Se pueden combinar.

```json theme={null}
{
  "query": "Análisis del cierre de OpenAI Sora",
  "include_domains": ["nytimes.com", "wsj.com", "bbc.com"],
  "exclude_domains": ["pinterest.com"]
}
```

## scrape\_options

Pasa `scrape_options` para raspar cada URL devuelta en paralelo e incrustar el contenido renderizado directamente en cada enlace. Esto ahorra un viaje de ida y vuelta por resultado en comparación con llamar a `/v1/searches` y `/v1/scrapes` por separado.

```json theme={null}
{
  "query": "¿Qué está pasando con el cierre de Sora de OpenAI?",
  "limit": 10,
  "scrape_options": {
    "formats": ["markdown"],
    "remove_css_selectors": "default",
    "timeout": 25
  }
}
```

| Campo                  | Tipo      | Predeterminado | Descripción                                                                                                                                                                                                                                             |
| ---------------------- | --------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `formats`              | string\[] | `["markdown"]` | Formatos de salida para adjuntar a cada enlace. Para `/v1/searches`, solo se admiten `"html"` y `"markdown"`. Pasa `["html", "markdown"]` para recibir ambos.                                                                                           |
| `remove_css_selectors` | string    | `"default"`    | Reenviado a `/v1/scrapes`. `"default"` elimina ruido de navegación/pie de página/script/estilo/svg/dialog. Usa `"none"` para desactivar, o pasa un array JSON de selectores a eliminar.                                                                 |
| `timeout`              | integer   | `25`           | Presupuesto de tiempo en **segundos** para toda la fase de raspado. Debe estar entre `1` y `60`. Después de que esto expire, la búsqueda se devuelve inmediatamente — los campos de contenido serán `null` para cualquier enlace que no haya terminado. |

### Comportamiento

* Todos los enlaces se raspan **en paralelo**. El `timeout` limita todo el lote, no cada enlace individual.
* Los fallos de raspado por enlace (errores de red, tiempos de espera individuales) dejan el `markdown_content` / `html_content` de ese enlace como `null` mientras que otros enlaces se devuelven normalmente.
* Si el `timeout` global expira antes de que todos los raspados terminen, la búsqueda responde inmediatamente con los enlaces que tiene — los raspados ya completados mantienen su contenido; los que están en proceso regresan con contenido `null`.
* Para URLs `reddit.com/.../comments/...`, la solicitud se enruta automáticamente a través del analizador `@olostep/reddit-post` y el JSON estructurado se renderiza en markdown limpio + HTML básico.
* Si el contenido combinado en línea supera los 9MB, los campos de contenido se anulan, `result.size_exceeded` se establece en `true`, y puedes obtener la carga completa desde `result.json_hosted_url`.

### Ejemplo con raspado

<CodeGroup>
  ```python Python theme={null}
  from olostep import Olostep

  client = Olostep(api_key="YOUR_REAL_KEY")

  search = client.searches.create(
      query="¿Qué está pasando con el cierre de Sora de OpenAI?",
      limit=5,
      scrape_options={"formats": ["markdown"], "timeout": 25},
  )

  for link in search.links:
      print(link["url"], "—", len(link.get("markdown_content") or ""), "chars")
  ```

  ```js Node theme={null}
  import Olostep, { Format } from 'olostep'

  const client = new Olostep({ apiKey: 'YOUR_REAL_KEY' })

  const search = await client.searches.create({
    query: "¿Qué está pasando con el cierre de Sora de OpenAI?",
    limit: 5,
    scrapeOptions: {
      formats: [Format.MARKDOWN],
      timeout: 25
    }
  })

  for (const link of search.links) {
    console.log(link.url, '—', (link.markdown_content || '').length, 'chars')
  }
  ```

  ```bash cURL theme={null}
  curl -s -X POST "https://api.olostep.com/v1/searches" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "What'"'"'s going on with OpenAI'"'"'s Sora shutting down?",
      "limit": 5,
      "scrape_options": {
        "formats": ["markdown"],
        "timeout": 25
      }
    }'
  ```

  ```js Node (API) theme={null}
  const res = await fetch('https://api.olostep.com/v1/searches', {
    method: 'POST',
    headers: { 'Authorization': 'Bearer <YOUR_API_KEY>', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      query: "¿Qué está pasando con el cierre de Sora de OpenAI?",
      limit: 5,
      scrape_options: {
        formats: ['markdown'],
        timeout: 25
      }
    })
  })
  const data = await res.json()
  for (const link of data.result.links) {
    console.log(link.url, '—', (link.markdown_content || '').length, 'chars')
  }
  ```

  ```python Python (API) theme={null}
  import requests, json

  endpoint = "https://api.olostep.com/v1/searches"
  payload = {
    "query": "¿Qué está pasando con el cierre de Sora de OpenAI?",
    "limit": 5,
    "scrape_options": {
      "formats": ["markdown"],
      "timeout": 25
    }
  }
  headers = {"Authorization": "Bearer <YOUR_API_KEY>", "Content-Type": "application/json"}

  response = requests.post(endpoint, json=payload, headers=headers)
  data = response.json()
  for link in data["result"]["links"]:
    print(link["url"], "—", len(link.get("markdown_content") or ""), "chars")
  ```
</CodeGroup>

## Respuesta

Recibirás un objeto `search` en respuesta. El objeto `search` contiene un `id`, tu `query` original, `credits_consumed`, y un `result` con una lista de `links`.

```json theme={null}
{
  "id": "search_9bi0sbj9xa",
  "object": "search",
  "created": 1760327323,
  "metadata": {},
  "query": "¿Qué está pasando con el cierre de Sora de OpenAI?",
  "credits_consumed": 10,
  "result": {
    "json_content": "...",
    "json_hosted_url": "https://olostep-storage.s3.us-east-1.amazonaws.com/search_9bi0sbj9xa.json",
    "size_exceeded": false,
    "credits_consumed": 10,
    "links": [
      {
        "url": "https://www.bbc.com/news/articles/c3w3e467ewqo",
        "title": "OpenAI cerrará la plataforma de video Sora",
        "description": "OpenAI dice que descontinuará su aplicación Sora...",
        "markdown_content": "# OpenAI cerrará la plataforma de video Sora\n\nOpenAI dice que descontinuará..."
      },
      {
        "url": "https://www.reddit.com/r/OutOfTheLoop/comments/1s2u847/whats_going_on_with_openais_sora_shutting_down/",
        "title": "¿Qué está pasando con el cierre de Sora de OpenAI?",
        "description": "Hilo de Reddit discutiendo el cierre.",
        "markdown_content": "# ¿Qué está pasando con el cierre de Sora de OpenAI?\n\n*r/OutOfTheLoop · u/rm-minus-r · hace 1 mes*\n\n..."
      }
    ]
  }
}
```

Cada enlace en `result.links` contiene:

| Campo              | Tipo   | Descripción                                                                                                                                                                            |
| ------------------ | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `url`              | string | La URL del resultado de la búsqueda.                                                                                                                                                   |
| `title`            | string | El título de la página de resultados.                                                                                                                                                  |
| `description`      | string | Un breve fragmento que describe el resultado.                                                                                                                                          |
| `markdown_content` | string | Contenido en markdown de la página. Solo presente cuando `scrape_options.formats` incluye `"markdown"`. `null` si el raspado falló, estaba vacío o alcanzó el tiempo de espera global. |
| `html_content`     | string | Contenido en HTML de la página. Solo presente cuando `scrape_options.formats` incluye `"html"`. `null` en caso de fallo/tiempo de espera.                                              |

El resultado completo también está disponible como un archivo JSON alojado en `result.json_hosted_url` — útil cuando `result.size_exceeded` es `true`.

## Recuperando una búsqueda pasada

`GET /v1/searches/{search_id}` devuelve lo que se guardó en el momento de la búsqueda, incluyendo cualquier contenido raspado. Es una lectura pura e idempotente — sin nuevo raspado, sin nueva facturación. Las búsquedas antiguas sin `scrape_options` simplemente no tienen campos de contenido por enlace.

<CodeGroup>
  ```python Python theme={null}
  from olostep import Olostep

  client = Olostep(api_key="YOUR_REAL_KEY")

  search = client.searches.get(search_id="search_9bi0sbj9xa")
  print(search.id, len(search.links))
  ```

  ```js Node theme={null}
  import Olostep from 'olostep'

  const client = new Olostep({ apiKey: 'YOUR_REAL_KEY' })

  const search = await client.searches.get('search_9bi0sbj9xa')
  console.log(search.id, search.links.length)
  ```

  ```bash cURL theme={null}
  curl -s "https://api.olostep.com/v1/searches/search_9bi0sbj9xa" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY"
  ```
</CodeGroup>

Consulta [Obtener Búsqueda](/api-reference/searches/get) para más detalles.

## Precios

Cada búsqueda cuesta **5 créditos** por la búsqueda en sí.

Cuando se proporciona `scrape_options`, cada página raspada se factura a la tarifa estándar de `/v1/scrapes` (típicamente 1 crédito por página; algunos analizadores cuestan más). El total se devuelve en `credits_consumed`.

Ejemplos:

| Solicitud                                          | `credits_consumed` |
| -------------------------------------------------- | ------------------ |
| Solo búsqueda                                      | `5`                |
| Búsqueda + 5 páginas raspadas (1 crédito cada una) | `10`               |
