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

# TypeScript SDK Live Data

> Fetch always-current Instagram data with cursor pagination using the Xpoz TypeScript SDK.

## What Live Data Is

Most SDK methods read from the Xpoz database, topping up from the crawler when results look stale. The `instagramLive` namespace is different: it **bypasses the database entirely** and fetches straight from the crawler API, so every call returns what Instagram is serving right now.

That trade-off is deliberate:

|              | `client.instagram`               | `client.instagramLive`       |
| ------------ | -------------------------------- | ---------------------------- |
| Source       | Database, topped up on demand    | Crawler API only             |
| Freshness    | May be minutes to days old       | Current                      |
| Speed        | Fast (indexed reads)             | Slower (live fetch per call) |
| Pagination   | Page numbers, totals, CSV export | Cursor only                  |
| Trial access | Yes                              | No                           |

Reach for `instagramLive` when freshness matters more than latency — checking a post's engagement right now, or pulling a follower list that changed this morning. For analysis over large historical sets, the database-backed methods are faster and support CSV export.

<Note>
  Live methods require a paid account. They always trigger a live fetch, so they are not available on trial access and throw `AuthenticationError` (HTTP 403).
</Note>

## No connect() Required

Live methods talk to the Xpoz REST API rather than the MCP server, so they work without opening an MCP session:

```typescript theme={null}
import { XpozClient } from "@xpoz/xpoz";

const client = new XpozClient({ apiKey: "your-api-key" });

// No await client.connect() needed for live methods
const page = await client.instagramLive.searchPosts("travel");
```

You still need `connect()` before using any other namespace (`client.instagram`, `client.twitter`, and so on).

## CursorResult

Live methods return a `CursorResult<T>` rather than the `PaginatedResult<T>` used elsewhere. The upstream API pages with an opaque **cursor** and reports no totals, so there is no page number, no `totalPages`, and no `getPage(n)`.

```typescript theme={null}
const page = await client.instagramLive.searchPosts("travel", { fields: ["id", "caption"] });

page.data;            // InstagramPost[] — this page
page.hasMore;         // another page is available upstream
page.nextPageCursor;  // opaque token for the next call
page.hasNextPage();   // boolean
```

### Navigating Pages

```typescript theme={null}
const page = await client.instagramLive.searchPosts("travel");

// One page at a time
if (page.hasNextPage()) {
  const next = await page.nextPage();
}

// Or walk every page
for await (const post of page.items()) {
  console.log(post.id);
}

// Or page by page
for await (const p of page.pages()) {
  console.log(`${p.data.length} posts`);
}
```

<Warning>
  Drive pagination off `hasMore` and the cursor — **never off the item count**. The upstream API may return a short or even empty page while `hasMore` is still `true`. Stopping when a page looks empty will cut your results short.
</Warning>

```typescript theme={null}
// Correct
let page = await client.instagramLive.searchPosts("travel");
while (page.hasNextPage()) {
  page = await page.nextPage();
  process(page.data);
}

// Wrong — stops early on a legitimately empty page
while (page.data.length > 0) {
  page = await page.nextPage();
}
```

## Methods

All methods accept `fields` to select which fields come back, and all paged methods accept `cursor` to resume from a previous response.

| Method                                                       | Returns                          |
| ------------------------------------------------------------ | -------------------------------- |
| `searchPosts(query, options?)`                               | `CursorResult<InstagramPost>`    |
| `getPostsByUser(identifier, options?)`                       | `CursorResult<InstagramPost>`    |
| `getPost(postId, options?)`                                  | `InstagramPost \| null`          |
| `getComments(postId, options?)`                              | `CursorResult<InstagramComment>` |
| `getPostInteractingUsers(postId, interactionType, options?)` | `CursorResult<InstagramUser>`    |
| `searchUsers(name, options?)`                                | `CursorResult<InstagramUser>`    |
| `getUser(identifier, options?)`                              | `InstagramUser \| null`          |
| `getUserConnections(identifier, connectionType, options?)`   | `CursorResult<InstagramUser>`    |

`interactionType` is `"commenters"` or `"likers"`. `connectionType` is `"followers"` or `"following"`. Both are typed unions, so invalid values fail at compile time.

`getPost` and `getUser` are single-item lookups and return the object directly, or `null` if nothing was found.

### Examples

```typescript theme={null}
import { XpozClient } from "@xpoz/xpoz";

const client = new XpozClient({ apiKey: "your-api-key" });

// Search posts by keyword
const posts = await client.instagramLive.searchPosts("travel", {
  fields: ["id", "caption", "likeCount"],
});

// A user's recent posts
const userPosts = await client.instagramLive.getPostsByUser("natgeo", { fields: ["id", "likeCount"] });

// Who liked a post
const likers = await client.instagramLive.getPostInteractingUsers(
  "3820275679950397949_495767729",
  "likers",
  { fields: ["id", "username"] }
);

// Who a user follows
const following = await client.instagramLive.getUserConnections("natgeo", "following", {
  fields: ["username"],
});

// Single lookups
const post = await client.instagramLive.getPost("3820275679950397949_495767729");
const user = await client.instagramLive.getUser("natgeo");
```

### Resuming From a Cursor

Cursors are opaque strings you can persist and reuse later, which is useful for long-running or resumable jobs:

```typescript theme={null}
const page = await client.instagramLive.searchPosts("travel");
const savedCursor = page.nextPageCursor;

// Later, in another process
const resumed = await client.instagramLive.searchPosts("travel", { cursor: savedCursor ?? undefined });
```

<Note>
  A cursor is only valid for the same query on the same endpoint. Reusing one elsewhere is rejected with a `400`.
</Note>

## Field Selection

Live methods accept the same `fields` values as their database-backed counterparts — see the [TypeScript SDK Reference](/sdks/typescript/reference) for the full Instagram field lists.

Live methods do **not** support `since` / `until` date filtering. The upstream API accepts only the query and a cursor, so use `client.instagram.searchPosts()` when you need a date range.

## Connection Details

Live methods talk to the Xpoz REST API (`https://api.xpoz.ai`) rather than the MCP server, using the same API key. Override the base URL when needed:

```typescript theme={null}
const client = new XpozClient({ apiKey: "your-api-key", apiUrl: "https://api.xpoz.ai" });
```

The `XPOZ_API_URL` environment variable does the same thing.
