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

# Paginación

Cuando solicitas una lista de elementos a Sorsa API, los resultados se devuelven por páginas. Para recuperar el conjunto completo, recorre las páginas mediante cursores hasta que no queden datos.

***

## Cómo funciona la paginación por cursor

Sorsa utiliza cursores en lugar de números de página. Este método es más fiable para datos sociales, donde se añade contenido continuamente y la paginación por desplazamiento puede omitir o duplicar resultados.

El procedimiento es el mismo en todos los endpoints paginados:

1. Envía la primera solicitud sin cursor.
2. La respuesta incluye los datos y un campo `next_cursor`.
3. Envía ese valor en la siguiente solicitud para obtener la próxima página.
4. Si `next_cursor` es `null` o no aparece, has llegado al final.

No todos los endpoints usan paginación. `/info`, `/tweet-info`, `/score` y `/about` devuelven un único objeto y no tienen cursor. Algunas listas también se devuelven en una sola respuesta, como `/info-batch`, `/tweet-info-bulk` y las listas de análisis de criptomonedas como `/top-followers`. Comprueba en la referencia si el endpoint admite `next_cursor`.

***

## Estructura de las respuestas

Las respuestas paginadas siguen uno de estos dos patrones:

```text theme={null}
{
  "users": [ ... ],
  "next_cursor": "DAABCgABF7Y..."
}
```

```text theme={null}
{
  "tweets": [ ... ],
  "next_cursor": "DAABCgABF7Y..."
}
```

En estas respuestas, los datos están en `users` o `tweets`. Trata `next_cursor` como un valor opaco: devuélvelo sin cambios y detente cuando sea nulo, esté vacío o no aparezca. No incrementes el cursor ni lo conviertas a un `Number` de JavaScript.

Consulta las estructuras y los esquemas de objetos en [Formato de respuesta](https://docs.sorsa.io/es/response-format).

***

## Cómo enviar los cursores

El campo siempre se llama `next_cursor`. Solo cambia dónde se envía: como parámetro de consulta en GET y dentro del cuerpo JSON en POST.

**Endpoints GET**, como `/followers`, `/follows` y `/list-tweets`:

```bash theme={null}
# First page
curl --request GET \
  --url 'https://api.sorsa.io/v3/followers?username=elonmusk' \
  --header 'ApiKey: YOUR_API_KEY'

# Next page
curl --request GET \
  --url 'https://api.sorsa.io/v3/followers?username=elonmusk&next_cursor=DAABCgABF7Y...' \
  --header 'ApiKey: YOUR_API_KEY'
```

**Endpoints POST**, como `/search-tweets`, `/user-tweets` y `/comments`:

```bash theme={null}
# First page
curl --request POST \
  --url 'https://api.sorsa.io/v3/search-tweets' \
  --header 'ApiKey: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"query": "bitcoin"}'

# Next page
curl --request POST \
  --url 'https://api.sorsa.io/v3/search-tweets' \
  --header 'ApiKey: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"query": "bitcoin", "next_cursor": "DAABCgABF7Y..."}'
```

***

## El tamaño de las páginas no es fijo

La cantidad de resultados por página puede variar debido a la naturaleza de los datos de X. Un endpoint que devuelve hasta 20 elementos puede devolver 18, 12 o incluso 5 en una página, aunque haya más datos en la siguiente.

**No utilices la cantidad de elementos para decidir si has llegado al final.** Una página con menos resultados de los esperados no indica que se hayan agotado los datos. Comprueba siempre `next_cursor`: si existe y no es `null`, quedan páginas por consultar.

***

## Ejemplos completos de paginación

Estos ejemplos acumulan los resultados en memoria. Para trabajos grandes, guarda cada página, elimina duplicados por ID de cadena y registra un punto de reanudación después de almacenarla. Mantén la misma cuenta, consulta, filtros y orden mientras utilices un cursor. Establece un presupuesto de páginas o solicitudes y detecta cursores repetidos para evitar bucles interminables. Una pausa en un bucle no coordina los demás procesos que comparten la clave.

**Python: recorrer seguidores (GET)**

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

API_KEY = "YOUR_API_KEY"

def fetch_all_followers(username):
    all_users = []
    cursor = None

    while True:
        params = {"username": username}
        if cursor:
            params["next_cursor"] = cursor

        response = requests.get(
            "https://api.sorsa.io/v3/followers",
            params=params,
            headers={"ApiKey": API_KEY},
            timeout=30,
        )
        response.raise_for_status()
        data = response.json()

        users = data.get("users", [])
        all_users.extend(users)
        print(f"Page fetched: {len(users)} users. Total so far: {len(all_users)}")

        cursor = data.get("next_cursor")
        if not cursor:
            break

        time.sleep(0.05)  # respect 20 req/s rate limit

    return all_users

followers = fetch_all_followers("elonmusk")
print(f"Done. {len(followers)} followers total.")
```

**Python: recorrer resultados de búsqueda (POST)**

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

API_KEY = "YOUR_API_KEY"

def search_all_tweets(query):
    all_tweets = []
    cursor = None

    while True:
        body = {"query": query}
        if cursor:
            body["next_cursor"] = cursor

        response = requests.post(
            "https://api.sorsa.io/v3/search-tweets",
            json=body,
            headers={"ApiKey": API_KEY},
            timeout=30,
        )
        response.raise_for_status()
        data = response.json()

        tweets = data.get("tweets", [])
        all_tweets.extend(tweets)
        print(f"Page fetched: {len(tweets)} tweets. Total so far: {len(all_tweets)}")

        cursor = data.get("next_cursor")
        if not cursor:
            break

        time.sleep(0.05)

    return all_tweets

results = search_all_tweets("bitcoin")
print(f"Done. {len(results)} tweets total.")
```

**JavaScript: recorrer seguidores (GET)**

```javascript theme={null}
async function fetchAllFollowers(username) {
  const API_KEY = "YOUR_API_KEY";
  const allUsers = [];
  let cursor = null;

  while (true) {
    const params = new URLSearchParams({ username });
    if (cursor) params.append("next_cursor", cursor);

    const response = await fetch(
      `https://api.sorsa.io/v3/followers?${params}`,
      { headers: { "ApiKey": API_KEY } }
    );
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    const data = await response.json();

    const users = data.users || [];
    allUsers.push(...users);
    console.log(`Page fetched: ${users.length} users. Total: ${allUsers.length}`);

    cursor = data.next_cursor;
    if (!cursor) break;

    await new Promise(r => setTimeout(r, 50));
  }

  return allUsers;
}
```

**JavaScript: recorrer resultados de búsqueda (POST)**

```javascript theme={null}
async function searchAllTweets(query) {
  const API_KEY = "YOUR_API_KEY";
  const allTweets = [];
  let cursor = null;

  while (true) {
    const body = { query };
    if (cursor) body.next_cursor = cursor;

    const response = await fetch("https://api.sorsa.io/v3/search-tweets", {
      method: "POST",
      headers: {
        "ApiKey": API_KEY,
        "Content-Type": "application/json"
      },
      body: JSON.stringify(body)
    });
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    const data = await response.json();

    const tweets = data.tweets || [];
    allTweets.push(...tweets);
    console.log(`Page fetched: ${tweets.length} tweets. Total: ${allTweets.length}`);

    cursor = data.next_cursor;
    if (!cursor) break;

    await new Promise(r => setTimeout(r, 50));
  }

  return allTweets;
}
```

***

## Paginación con gestión de errores

En producción, combina la paginación con reintentos para que el fallo de una página no interrumpa toda la recopilación. Consulta la referencia completa en [Códigos de error](https://docs.sorsa.io/es/error-codes).

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

API_KEY = "YOUR_API_KEY"

def paginate_with_retries(username, max_retries=3):
    all_users = []
    cursor = None

    while True:
        params = {"username": username}
        if cursor:
            params["next_cursor"] = cursor

        for attempt in range(max_retries):
            response = requests.get(
                "https://api.sorsa.io/v3/followers",
                params=params,
                headers={"ApiKey": API_KEY},
                timeout=30,
            )

            if response.status_code == 200:
                break
            elif response.status_code == 429:
                time.sleep(1)
                continue
            elif response.status_code >= 500:
                time.sleep(2)
                continue
            else:
                raise Exception(f"Error {response.status_code}: {response.text}")
        else:
            raise Exception("Max retries exceeded")

        data = response.json()
        all_users.extend(data.get("users", []))

        cursor = data.get("next_cursor")
        if not cursor:
            break

        time.sleep(0.05)

    return all_users
```

***

## Próximos pasos

* [Límites de solicitudes](https://docs.sorsa.io/es/rate-limits): cómo respetar las 20 solicitudes por segundo al recorrer grandes conjuntos de datos.
* [Códigos de error](https://docs.sorsa.io/es/error-codes): gestión de errores 429 y otros fallos en los bucles de paginación.
* [Formato de respuesta](https://docs.sorsa.io/es/response-format): esquemas completos de User y Tweet.
