---
title: "nua.list()"
description: "Reference for nua.list(), which processes an array of records in one request and returns one typed output per row."
canonical: "https://nuabase.com/docs/api/list/"
---

> Documentation index: https://nuabase.com/llms.txt

`list()` processes an array of records in a single LLM request and returns one output per row.

## Signature

```typescript
nua.list<
  S extends z.ZodTypeAny,
  TData extends Record<string, unknown>,
  PK extends keyof TData & string,
  Name extends string,
>(
  prompt: string,
  options: ListOptions<S, TData, PK, Name>
): Promise<CastResult<ListResultRow<S, PK, Name>[]>>
```

## Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `prompt` | `string` | Yes | Instructions applied to each row. Keep it constant across calls. |
| `options.input` | `TData[]` | Yes | Array of objects to process. |
| `options.primaryKey` | `keyof TData` | Yes | Name of a property that uniquely identifies each row. |
| `options.output.name` | `string` | Yes | Property name for the generated value on each result row. |
| `options.output.schema` | Zod schema | Yes | The shape of the value generated for each row. |
| `options.model` | `ModelInput` | No | Overrides the client's default model for this call. |

## Result rows

Each result row has two properties: the primary key, echoed back by the model, and the output under `output.name`.

```typescript
const result = await nua.list('Analyze the sentiment of each review', {
  input: [
    { reviewId: 'r1', text: 'Great product! Exceeded expectations.' },
    { reviewId: 'r2', text: 'Terrible service. Never buying again.' },
  ],
  primaryKey: 'reviewId',
  output: { name: 'sentiment', schema: z.enum(['positive', 'neutral', 'negative']) },
});

// result.data:
// [
//   { reviewId: 'r1', sentiment: 'positive' },
//   { reviewId: 'r2', sentiment: 'negative' },
// ]
```

Rows are typed from the primary key and the output's name. Here each row is `{ reviewId: string | number; sentiment: 'positive' | 'neutral' | 'negative' }`, so `row.sentiment` is typed and `row.sentment` is a type error. Pass `output.name` as a string literal, as above, to get this type. A name in a variable typed as `string` gives rows whose every other property has the output's type.

## The primary key

The model may return rows in a different order than the input, so always match result rows to inputs by primary key, never by array index:

```typescript
const byId = new Map(reviews.map((r) => [r.reviewId, r]));

for (const row of result.data) {
  const review = byId.get(row.reviewId);
  // ...
}
```

Each input object must be an object containing the `primaryKey` property and at least one other property. Choose a field that is unique within the array, such as `id`, `email`, or `slug`. Values may be strings or numbers.

Nuabase parses every returned row with your schema, but doesn't require that every input row comes back. If a missing row would matter, compare the returned keys against your input.

## Examples

### Lead enrichment

```typescript
const EnrichmentSchema = z.object({
  companySize: z.enum(['solo', 'small', 'medium', 'enterprise']),
  priority: z.enum(['low', 'medium', 'high']),
  recommendedPlan: z.string(),
});

const result = await nua.list('Analyze each lead and recommend a plan', {
  input: [
    { id: 'lead-1', notes: 'CEO at Acme Corp, interested in enterprise plan' },
    { id: 'lead-2', notes: 'Freelancer, looking for starter tier' },
    { id: 'lead-3', notes: 'CTO at growing startup, evaluating options' },
  ],
  primaryKey: 'id',
  output: { name: 'enrichment', schema: EnrichmentSchema },
});

if (result.success) {
  for (const row of result.data) {
    console.log(`${row.id}: ${row.enrichment.priority} - ${row.enrichment.recommendedPlan}`);
  }
}
```

### Content tagging

```typescript
const TagSchema = z.object({
  tags: z.array(z.string()).max(5),
  difficulty: z.enum(['beginner', 'intermediate', 'advanced']),
});

const result = await nua.list('Generate tags and a difficulty level for each article', {
  input: articles, // [{ slug, title, content }, ...]
  primaryKey: 'slug',
  output: { name: 'metadata', schema: TagSchema },
});
```

### Routing support email

```typescript
const RouteSchema = z.object({
  department: z.enum(['support', 'sales', 'billing', 'engineering']),
  urgency: z.enum(['low', 'normal', 'high', 'critical']),
});

const result = await nua.list('Classify each email for routing', {
  input: emails, // [{ messageId, subject, body }, ...]
  primaryKey: 'messageId',
  output: { name: 'routing', schema: RouteSchema },
});
```

## Large arrays

All rows go into one request, so the input must fit the model's context, and the output for every row must fit in one response. On API providers a response is capped at 4096 output tokens. For large datasets, split the array into chunks and call `list()` per chunk:

```typescript
const chunkSize = 50;
const rows = [];

for (let i = 0; i < leads.length; i += chunkSize) {
  const result = await nua.list(prompt, {
    input: leads.slice(i, i + chunkSize),
    primaryKey: 'id',
    output,
  });
  if (!result.success) throw new Error(result.error);
  rows.push(...result.data);
}
```

Using the same `prompt` for every chunk lets the provider reuse its [prompt cache](/docs/how-it-works.md#prompt-caching). You can also run chunks in parallel with `Promise.all`; a local agent runs at most `concurrency` (default 2) processes at once and queues the rest.
