> For a complete page index of the Captain API documentation, fetch https://docs.captain.dev/llms.txt?excludeSpec=true

# Create Index

POST https://acme.captain.dev/v1/indexes
Content-Type: application/json

Create an empty index. Name it, and optionally give field types. Fields left out are typed from the first records added. Then add records to it.

Reference: https://docs.captain.dev/reference/product-search/indexes/create-index

## Authentication

- `Authorization` header (bearer token, required) — A write key, issued by Captain. Server side only. Sent as Authorization: Bearer \<key>.

## Request

### Headers

- `Idempotency-Key` (string, optional) — Replays within 24 hours return the stored response (header Idempotent-Replayed: true). The same key on a different request answers 422. A concurrent duplicate answers 409. Failed requests do not keep the key.

### Body (application/json)

This endpoint expects an IndexCreate.

- `name` (string, required)
- `id_field` (string, optional, default: id) — field holding each record's unique id (objectID is accepted when this is id)
- `fields` (map from string to IndexCreateFields, optional) — optional field types, by field name. A field left out is typed from the first records added. The same as settings.fields
- `expected_records` (integer, optional) — how many records this index will hold: the tenant's capacity forecast counts it at once, so a resize can start before the upload lands (real records take over once they pass it)
- `expected_bytes` (integer, optional) — the source JSON those records will total, for the same forecast

## Response

### 201

created

- `name` (string, optional) — the index name
- `id_field` (string, optional) — Create Index only: the field that holds each record's id
- `status` (enum, optional) — empty: no records yet. pending: changes were accepted and are being applied. building: Captain is refreshing suggestions and spelling in the background, and search serves as usual. ready: every change is applied.
  - Allowed values: `empty`, `pending`, `building`, `ready`
- `record_count` (integer, optional) — Records the index holds, built or not.
- `staged_records` (integer, optional) — the same value as record_count, under its older name
- `staged_documents` (integer, optional) — the same value as record_count, under its older name
- `pending_changes` (boolean, optional) — True while accepted changes are still being applied.
- `staged_changes_since_build` (boolean, optional) — the same value as pending_changes, under its older name
- `object_search` (boolean, optional) — true for an index with typed fields: every index Create Index makes, and an Algolia import sent with object_search true. Settings apply to these
- `build_id` (string, optional, nullable) — the build search serves; null before the first one
- `last_job` (string, optional, nullable) — the id of the index's latest job, for Get Job
- `last_build_at` (integer, optional, nullable) — unix seconds the serving build finished; null when nothing is served, or for a build whose finish time was not recorded
- `created_at` (integer, optional) — unix seconds
- `serving` (IndexServing, optional, nullable) — The build search is serving, and how many records it holds.

## Types

### IndexCreateFields

- `type` (enum, required)
  - Allowed values: `text`, `keyword`, `enum`, `identifier`, `number`, `date`, `geo`, `bool`, `url`, `children`
- `facet` (boolean, optional) — count this field's values in facets
- `unit` (string, optional) — for a number, such as usd or kg
- `values` (list of string, optional) — for an enum, the allowed values

### IndexServing

The build search is serving, and how many records it holds.

- `build` (string, optional, nullable)
- `records` (integer, optional)
- `documents` (integer, optional) — the same value as records, under its older name

## Examples

### Create an index

**Request**

```json
{
  "name": "products"
}
```

**Response**

```json
{
  "name": "products",
  "id_field": "id",
  "status": "empty",
  "record_count": 0,
  "staged_records": 0,
  "staged_documents": 0,
  "pending_changes": false,
  "staged_changes_since_build": false,
  "object_search": true,
  "build_id": null,
  "last_job": null,
  "last_build_at": null,
  "created_at": 1759093140,
  "serving": null
}
```

**SDK Code**

```python Python REST
import json
import requests

BASE_URL = "https://acme.captain.dev"
API_KEY = "your_write_key"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

response = requests.post(
    f"{BASE_URL}/v1/indexes",
    headers=headers,
    json={
        "name": "products",
    },
    timeout=30.0,
)

print(json.dumps(response.json(), indent=2))
```

```typescript TypeScript REST
const BASE_URL = "https://acme.captain.dev";
const API_KEY = "your_write_key";

const response = await fetch(`${BASE_URL}/v1/indexes`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "products",
  }),
});

const result = await response.json();
console.log(JSON.stringify(result, null, 2));
```

### Create an index with field types

**Request**

```json
{
  "name": "products",
  "fields": {
    "title": {
      "type": "text"
    },
    "description": {
      "type": "text"
    },
    "brand": {
      "type": "keyword",
      "facet": true
    },
    "category": {
      "type": "keyword",
      "facet": true
    },
    "price": {
      "type": "number",
      "unit": "usd"
    },
    "rating": {
      "type": "number"
    },
    "image_url": {
      "type": "url"
    }
  }
}
```

**Response**

```json
{
  "name": "products",
  "id_field": "id",
  "status": "empty",
  "record_count": 0,
  "staged_records": 0,
  "staged_documents": 0,
  "pending_changes": false,
  "staged_changes_since_build": false,
  "object_search": true,
  "build_id": null,
  "last_job": null,
  "last_build_at": null,
  "created_at": 1759093140,
  "serving": null
}
```

**SDK Code**

```python Python REST
import json
import requests

BASE_URL = "https://acme.captain.dev"
API_KEY = "your_write_key"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

response = requests.post(
    f"{BASE_URL}/v1/indexes",
    headers=headers,
    json={
        "name": "products",
        "fields": {
            "title": {"type": "text"},
            "description": {"type": "text"},
            "brand": {"type": "keyword", "facet": True},
            "category": {"type": "keyword", "facet": True},
            "price": {"type": "number", "unit": "usd"},
            "rating": {"type": "number"},
            "image_url": {"type": "url"},
        },
    },
    timeout=30.0,
)

print(json.dumps(response.json(), indent=2))
```

```typescript TypeScript REST
const BASE_URL = "https://acme.captain.dev";
const API_KEY = "your_write_key";

const response = await fetch(`${BASE_URL}/v1/indexes`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "products",
    fields: {
      title: { type: "text" },
      description: { type: "text" },
      brand: { type: "keyword", facet: true },
      category: { type: "keyword", facet: true },
      price: { type: "number", unit: "usd" },
      rating: { type: "number" },
      image_url: { type: "url" },
    },
  }),
});

const result = await response.json();
console.log(JSON.stringify(result, null, 2));
```