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

# Pagination

> How to paginate through API results

# Pagination

The SuprSonic API uses cursor-based pagination with HATEOAS links to help you efficiently navigate through large datasets. All list endpoints support pagination with consistent parameters and response formats.

## Pagination Parameters

| Parameter | Type    | Default | Description            |
| --------- | ------- | ------- | ---------------------- |
| `page`    | integer | `1`     | Page number (1-based)  |
| `limit`   | integer | `20`    | Items per page (1-100) |

## Example Request

```bash theme={null}
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://www.suprsonic.com/api/v1/articles?page=2&limit=10"
```

## Pagination Response Structure

All paginated responses include metadata and navigation links:

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "title": "Article Title",
      // ... article data
    }
  ],
  "meta": {
    "pagination": {
      "page": 2,
      "limit": 10,
      "total": 145,
      "totalPages": 15,
      "hasNext": true,
      "hasPrev": true
    },
    "links": {
      "self": "/api/v1/articles?page=2&limit=10",
      "first": "/api/v1/articles?page=1&limit=10",
      "prev": "/api/v1/articles?page=1&limit=10",
      "next": "/api/v1/articles?page=3&limit=10",
      "last": "/api/v1/articles?page=15&limit=10"
    }
  }
}
```

## Pagination Metadata

### Pagination Object

| Field        | Type    | Description                     |
| ------------ | ------- | ------------------------------- |
| `page`       | integer | Current page number             |
| `limit`      | integer | Items per page                  |
| `total`      | integer | Total number of items           |
| `totalPages` | integer | Total number of pages           |
| `hasNext`    | boolean | Whether there's a next page     |
| `hasPrev`    | boolean | Whether there's a previous page |

### HATEOAS Links

Navigation links preserve all query parameters from your original request:

| Link    | Description               |
| ------- | ------------------------- |
| `self`  | Current page              |
| `first` | First page                |
| `prev`  | Previous page (if exists) |
| `next`  | Next page (if exists)     |
| `last`  | Last page                 |

## Pagination with Filters

When using filters, pagination links automatically preserve your search parameters:

```bash theme={null}
# Request with filters
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://www.suprsonic.com/api/v1/articles?q=SEO&tags=marketing&status=published&page=1&limit=10"
```

```json theme={null}
{
  "success": true,
  "data": [...],
  "meta": {
    "pagination": {...},
    "links": {
      "self": "/api/v1/articles?q=SEO&tags=marketing&status=published&page=1&limit=10",
      "next": "/api/v1/articles?q=SEO&tags=marketing&status=published&page=2&limit=10"
    }
  }
}
```

## Implementation Examples

### JavaScript/Node.js

```javascript theme={null}
class SuprSonicClient {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://www.suprsonic.com/api/v1';
  }

  async getAllArticles(filters = {}) {
    let allArticles = [];
    let page = 1;
    let hasMore = true;

    while (hasMore) {
      const response = await this.getArticles({ 
        ...filters, 
        page, 
        limit: 100 
      });
      
      allArticles.push(...response.data);
      hasMore = response.meta.pagination.hasNext;
      page++;
    }

    return allArticles;
  }

  async getArticles(params = {}) {
    const queryString = new URLSearchParams(params).toString();
    const url = `${this.baseUrl}/articles?${queryString}`;
    
    const response = await fetch(url, {
      headers: { 'Authorization': `Bearer ${this.apiKey}` }
    });
    
    if (!response.ok) {
      throw new Error(`API Error: ${response.status}`);
    }
    
    return await response.json();
  }

  // Iterator pattern for memory efficiency
  async* iterateArticles(filters = {}) {
    let page = 1;
    let hasMore = true;

    while (hasMore) {
      const response = await this.getArticles({ 
        ...filters, 
        page, 
        limit: 50 
      });
      
      for (const article of response.data) {
        yield article;
      }
      
      hasMore = response.meta.pagination.hasNext;
      page++;
    }
  }
}

// Usage examples
const client = new SuprSonicClient('your-api-key');

// Get all articles (automatic pagination)
const allArticles = await client.getAllArticles({ 
  status: 'published' 
});

// Process articles one at a time (memory efficient)
for await (const article of client.iterateArticles({ q: 'SEO' })) {
  console.log(`Processing: ${article.title}`);
}

// Manual pagination with HATEOAS links
let response = await client.getArticles({ limit: 20 });
while (response.meta.pagination.hasNext) {
  // Process current page
  console.log(`Page ${response.meta.pagination.page}: ${response.data.length} articles`);
  
  // Use HATEOAS link for next page
  const nextUrl = response.meta.links.next;
          const nextResponse = await fetch(`https://www.suprsonic.com${nextUrl}`, {
    headers: { 'Authorization': `Bearer ${client.apiKey}` }
  });
  response = await nextResponse.json();
}
```

### Python

```python theme={null}
import requests
from typing import Generator, Dict, Any, Optional

class SuprSonicClient:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://www.suprsonic.com/api/v1"
        self.session = requests.Session()
        self.session.headers.update({
            "Authorization": f"Bearer {api_key}"
        })

    def get_articles(self, **params) -> Dict[str, Any]:
        """Get a single page of articles."""
        response = self.session.get(f"{self.base_url}/articles", params=params)
        response.raise_for_status()
        return response.json()

    def iterate_all_articles(self, **filters) -> Generator[Dict[str, Any], None, None]:
        """Iterate through all articles across all pages."""
        page = 1
        while True:
            response = self.get_articles(page=page, limit=100, **filters)
            
            for article in response["data"]:
                yield article
            
            if not response["meta"]["pagination"]["hasNext"]:
                break
            page += 1

    def get_all_articles(self, **filters) -> list[Dict[str, Any]]:
        """Get all articles as a list."""
        return list(self.iterate_all_articles(**filters))

# Usage
client = SuprSonicClient("your-api-key")

# Process articles one by one (memory efficient)
for article in client.iterate_all_articles(status="published"):
    print(f"Processing: {article['title']}")

# Get all articles at once
all_articles = client.get_all_articles(tags="seo,marketing")
print(f"Found {len(all_articles)} articles")

# Manual pagination
response = client.get_articles(limit=20)
print(f"Page {response['meta']['pagination']['page']} of {response['meta']['pagination']['totalPages']}")
```

## Best Practices

### 1. Use Appropriate Page Sizes

```javascript theme={null}
// Good: Reasonable page sizes
const response = await getArticles({ limit: 20 }); // UI pagination
const response = await getArticles({ limit: 100 }); // Bulk processing

// Avoid: Too small (many requests)
const response = await getArticles({ limit: 5 });

// Avoid: Too large (slow responses)
const response = await getArticles({ limit: 1000 });
```

### 2. Handle Pagination Errors

```javascript theme={null}
async function safePagination(client, filters) {
  const results = [];
  let page = 1;
  const maxRetries = 3;

  while (true) {
    try {
      const response = await client.getArticles({ 
        ...filters, 
        page, 
        limit: 50 
      });
      
      results.push(...response.data);
      
      if (!response.meta.pagination.hasNext) break;
      page++;
      
    } catch (error) {
      if (error.status === 429) {
        // Rate limited - wait and retry
        await new Promise(resolve => setTimeout(resolve, 1000));
        continue;
      }
      throw error;
    }
  }
  
  return results;
}
```

### 3. Cache and Store Results

```javascript theme={null}
class CachedSuprSonicClient {
  constructor(apiKey, cacheSize = 1000) {
    this.client = new SuprSonicClient(apiKey);
    this.cache = new Map();
    this.cacheSize = cacheSize;
  }

  async getArticles(params = {}) {
    const cacheKey = JSON.stringify(params);
    
    if (this.cache.has(cacheKey)) {
      return this.cache.get(cacheKey);
    }
    
    const response = await this.client.getArticles(params);
    
    // Simple LRU cache
    if (this.cache.size >= this.cacheSize) {
      const firstKey = this.cache.keys().next().value;
      this.cache.delete(firstKey);
    }
    
    this.cache.set(cacheKey, response);
    return response;
  }
}
```

## Performance Considerations

* **Optimize page size**: Balance between request frequency and response time
* **Use filters**: Reduce total items to paginate through
* **Implement caching**: Cache responses for frequently accessed pages
* **Monitor rate limits**: Implement exponential backoff for rate-limited requests
* **Stream processing**: Use iterators for large datasets to avoid memory issues

## Common Patterns

### Search with Pagination

```bash theme={null}
# Search articles and paginate results
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://www.suprsonic.com/api/v1/articles?q=content+marketing&page=1&limit=25"
```

### Filter by Multiple Tags

```bash theme={null}
# Get articles with specific tags
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://www.suprsonic.com/api/v1/articles?tags=seo,marketing,content&page=1&limit=50"
```

### Status-Based Pagination

```bash theme={null}
# Paginate through draft articles
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://www.suprsonic.com/api/v1/articles?status=draft&page=1&limit=10"
```
