# Pagination & Caching

**Pagination**

The `pagination` object is identical on all three list endpoints and is always present, even when the page is empty.

```json
{
  "total": 42,
  "limit": 20,
  "offset": 0,
  "total_pages": 3,
  "current_page": 1,
  "has_next": true,
  "has_prev": false
}
```

| Field | Meaning |
|-------|---------|
| `total` | Distinct rows matching every filter |
| `limit` | Page size actually applied |
| `offset` | Rows skipped |
| `total_pages` | Pages at the current `limit` |
| `current_page` | Derived from `offset / limit`, 1-based |
| `has_next` / `has_prev` | Whether another page exists in that direction |

- `limit` above 100 is silently clamped to 100. A zero, negative, or non-numeric value falls back to 10.
- A negative or non-numeric `offset` falls back to 0.
- `total` counts distinct rows, so it is safe to drive an infinite scroll from it.

Walking every page:

```javascript
async function fetchAllExercises(params = {}) {
  const items = [];
  let offset = 0;
  const limit = 50;

  for (;;) {
    const query = new URLSearchParams({ ...params, limit, offset });
    const res = await fetch(
      `https://data.kinestex.com/api/exercises/client?` + query,
      { headers: { "x-api-key": process.env.KINESTEX_API_KEY, Language: "en" } }
    );
    if (!res.ok) throw new Error(`Content API ` + res.status);

    const page = await res.json();
    items.push(...page.exercises);
    if (!page.pagination.has_next) return items;
    offset += limit;
  }
}
```

**Caching**

List responses are cached for up to **5 minutes**, keyed per company, per language, and per exact query string, so two different filter combinations never serve each other's results. Single-item responses are cached for up to **15 minutes**. Both caches are invalidated when the underlying content is edited, so in normal use you see changes quickly; allow for the window when you write tests that create content and immediately list it.

**Performance tips**

- Keep `include_exercises` and `include_weeks` at their `false` default for browsing lists. Turn them on only for the screen that actually plays the content.
- Prefer server-side filters over fetching everything and filtering client-side. Every filter listed here runs in SQL.
- Keep page sizes moderate (10 to 20 for a UI, up to 100 for a sync job).
- Use `translation_languages` as a filter when you need a fully localized catalog, rather than fetching everything and discarding items with a null `translation`.

---
Source: https://www.kinestex.com/docs/content-api-v2/content-api-v2-pagination · Index: https://www.kinestex.com/llms.txt
