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

# Import From Algolia

POST https://acme.captain.dev/v1/indexes/{index}/import/algolia
Content-Type: application/json

Copy an Algolia index into this index: send an export of it, or read access to it. Importing into an index that already has records deletes all of them and adds the Algolia records in their place. Read access answers 202 with an import job that builds the index when it ends. An export answers 200 with what was mapped; send `build: true` to start the build too. Importing into a new index takes a write key for every index. Algolia keys are never stored.

Reference: https://docs.captain.dev/reference/product-search/indexes/import-from-algolia

## Authentication

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

## Request

### Path parameters

- `index` (string, required) — index name

### 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 Indexes_import_from_algolia_Request.

- `Indexes_import_from_algolia_Request`

## Response

### 200

An export: the records were added, with what was mapped. The build starts only with build: true (build_job).

- `schema` (Schema, optional) — flexible schema: one entry per (dotted) field
- `facets` (list of string, optional)
- `report` (ImportResultReport, optional)
- `counts` (ImportResultCounts, optional) — per section \{mapped, approximated, not\_mapped}, and records
- `fields` (map from string to ImportResultFields, optional)
- `notes` (list of string, optional) — what could not be read (for example no settings ACL)
- `source` (string, optional)
- `records` (integer, optional)
- `build_job` (string, optional, nullable)
- `build_note` (string, optional)
- `object_search` (boolean, optional) — true: an import sent with object_search true (settings and effective instead of schema)
- `dry_run` (boolean, optional) — true: a preview. Nothing was stored; records counts the records that would be added
- `settings` (ImportResultSettings, optional) — with object_search: the settings the Algolia settings mapped to
- `effective` (ImportResultEffective, optional) — object_search: the field table the next build uses

### 202

Read access: the import job. Get Job reports its progress, and result.build_job names the build it starts.

- `job` (Job, optional)

## Types

### export

- `records` (list of V1IndexesIndexImportAlgoliaPostRequestBodyContentApplicationJsonSchemaOneOf0RecordsItems, required) — the Algolia records, as Algolia's export writes them, each with its objectID
- `settings` (V1IndexesIndexImportAlgoliaPostRequestBodyContentApplicationJsonSchemaOneOf0Settings, optional) — the Algolia index settings
- `synonyms` (list of V1IndexesIndexImportAlgoliaPostRequestBodyContentApplicationJsonSchemaOneOf0SynonymsItems, optional) — the Algolia synonyms
- `rules` (list of V1IndexesIndexImportAlgoliaPostRequestBodyContentApplicationJsonSchemaOneOf0RulesItems, optional) — the Algolia rules
- `build` (boolean, optional, default: false) — true: start the build once the records are added. Default false
- `dry_run` (boolean, optional, default: false) — true: map and check everything as an import would, and store nothing. The reply shows what would be mapped
- `object_search` (boolean, optional, default: false) — true: set the index up with typed fields from the Algolia settings, the same kind of index Create Index makes, so Get Settings shows them and Update Settings changes them. Default false: an older mapping with no field settings. Send true

### pull

- `app_id` (string, required) — the Algolia application id
- `api_key` (string, required) — an Algolia key with read permissions only (browse, and settings to bring settings, synonyms and rules). A key that can write is refused with 400 ALGOLIA_KEY_CAN_WRITE before anything is read. Used for this import only, never stored, logged or returned
- `index_name` (string, required) — the Algolia index to read
- `build` (boolean, optional, default: true) — false: add the records and don't build. Default true: the index builds when the import ends
- `object_search` (boolean, optional, default: false) — true: set the index up with typed fields from the Algolia settings, the same kind of index Create Index makes, so Get Settings shows them and Update Settings changes them. Default false: an older mapping with no field settings. Send true

### Schema

flexible schema: one entry per (dotted) field

- `fields` (map from string to SchemaFields, required)
- `n` (integer, optional)

### ImportResultReport

- `settings` (list of ImportRow, optional)
- `synonyms` (list of ImportRow, optional)
- `rules` (list of ImportRow, optional)

### ImportResultCounts

per section \{mapped, approximated, not\_mapped}, and records

### ImportResultFields

- `type` (string, optional)
- `role` (string, optional)

### ImportResultSettings

with object_search: the settings the Algolia settings mapped to

### ImportResultEffective

object_search: the field table the next build uses

### Job

- `id` (string, optional)
- `type` (enum, optional) — index.write: record updates and deletes through the ordered update log, one job per burst
  - Allowed values: `index.build`, `index.delete`, `index.import`, `index.write`
- `index` (string, optional)
- `seq_from` (integer, optional) — index.write: the first update this job carries
- `seq_to` (integer, optional) — index.write: the last update this job carries
- `records` (integer, optional) — index.write: records written
- `status` (enum, optional)
  - Allowed values: `queued`, `running`, `importing`, `succeeded`, `failed`
- `created_at` (integer, optional)
- `started_at` (integer, optional, nullable)
- `finished_at` (integer, optional, nullable)
- `stages` (list of JobStagesItems, optional)
- `progress` (BuildProgress, optional) — index.build: how far the build has got. records_done counts the records the running step has done; a step that doesn't count records (tables, verify, publish) reports every record done
- `build_id` (string, optional)
- `error` (string, optional, nullable)
- `error_code` (string, optional) — why a failed job failed; error says it in words to show. A build: BAD_VALUES (more than half of a field's values don't fit its declared type; see bad_values), BUILD_QUALITY (the build's checks refused it; the previous build keeps serving), NO_RECORDS (every record was deleted or cleared before the build read them), CANCELLED (the index is being deleted), ENGINE_ERROR (a fault on Captain's side, in the build or in running the job; the index keeps serving its previous build; retry). An index delete: DELETE_FAILED (Captain couldn't delete the index; retry). An Algolia import: the Algolia code (ALGOLIA_APP_NOT_FOUND, ALGOLIA_UNREACHABLE, ALGOLIA_INDEX_NOT_FOUND, ALGOLIA_FORBIDDEN, ALGOLIA_TIMEOUT, ALGOLIA_ERROR, INDEX_TOO_LARGE, VALIDATION_ERROR) or IMPORT_FAILED (a fault on Captain's side; nothing was changed; retry)
- `bad_values` (list of JobBadValuesItems, optional) — with error_code BAD_VALUES: per field, count of bad values, of how many, and up to 3 examples
- `warnings` (list of JobWarningsItems, optional) — \{code, message}. Settings resolved by a rule (the same warnings GET settings shows). Any build: SETTLE\_TIMEOUT (the build is live, but its search results are still settling and may shift for a few minutes; no action needed)
- `facet_report` (list of JobFacetReportItems, optional) — per number or date facet, whether it has value counts and why
- `result` (JobResult, optional, nullable)

### V1IndexesIndexImportAlgoliaPostRequestBodyContentApplicationJsonSchemaOneOf0RecordsItems

### V1IndexesIndexImportAlgoliaPostRequestBodyContentApplicationJsonSchemaOneOf0Settings

the Algolia index settings

### V1IndexesIndexImportAlgoliaPostRequestBodyContentApplicationJsonSchemaOneOf0SynonymsItems

### V1IndexesIndexImportAlgoliaPostRequestBodyContentApplicationJsonSchemaOneOf0RulesItems

### SchemaFields

- `type` (enum, required)
  - Allowed values: `text`, `keyword`, `identifier`, `number`, `measure`, `bool`, `keyword_list`, `identifier_list`, `url`, `object`, `empty`
- `role` (enum, required)
  - Allowed values: `id`, `title`, `brand`, `category`, `category_path`, `description`, `identifier`, `attribute`, `attribute_map`, `price`, `rating`, `review_count`, `image_url`, `skip`
- `facet` (boolean, optional)
- `unit` (string, optional)
- `coverage` (double, optional)
- `role_source` (enum, optional) — present when the role was inferred from the field's name (price, rating, brand ...), not from its values: check it in the preview
  - Allowed values: `name`

### ImportRow

- `item` (string, optional)
- `status` (enum, optional)
  - Allowed values: `mapped`, `approximated`, `not_mapped`
- `why` (string, optional)

### JobStagesItems

- `name` (enum, optional)
  - Allowed values: `schema`, `dense`, `splade`, `upload`, `tables`, `verify`, `publish`, `replicate`, `drop`, `pull`, `read`, `map`, `add`, `validate`, `embed`, `upsert`, `refresh`
- `status` (enum, optional)
  - Allowed values: `queued`, `pending`, `running`, `done`, `failed`, `skipped`
- `count` (integer, optional)
- `ms` (double, optional)
- `started_at` (double, optional)
- `finished_at` (double, optional)
- `reason` (string, optional) — why this step was skipped, in words
- `items` (integer, optional)
- `of` (integer, optional)
- `seconds` (integer, optional)

### BuildProgress

index.build: how far the build has got. records_done counts the records the running step has done; a step that doesn't count records (tables, verify, publish) reports every record done

- `step` (string, optional) — the step running now, as named in stages; queued before the first, done after the last
- `step_number` (integer, optional) — its place in the build, from 1
- `steps` (integer, optional) — steps in this build
- `records_done` (integer, optional)
- `records_total` (integer, optional, nullable)

### JobBadValuesItems

### JobWarningsItems

### JobFacetReportItems

- `field` (string, optional)
- `type` (string, optional)
- `distinct` (integer, optional, nullable)
- `facet_counts` (boolean, optional)
- `reason` (string, optional)

### JobResult

- `alias` (string, optional)
- `build` (string, optional)
- `physical` (string, optional)
- `previous` (string, optional, nullable)
- `records` (integer, optional)
- `facets` (list of string, optional)
- `seconds` (integer, optional)
- `checks` (JobResultChecks, optional)
- `constraints` (JobResultConstraints, optional) — index.build: whether measurement queries ('under 30 inches') are scored on the new build, and why not
- `report` (string, optional) — index.import: where the import report is
- `build_job` (string, optional, nullable) — index.import: the build job it queued

### JobResultChecks

### JobResultConstraints

index.build: whether measurement queries ('under 30 inches') are scored on the new build, and why not

- `ranking` (boolean, required)
- `reason` (string, required, nullable)

## Examples

**Request**

```json
{
  "app_id": "YOUR_ALGOLIA_APP_ID",
  "api_key": "YOUR_ALGOLIA_READ_KEY",
  "index_name": "prod_products",
  "object_search": true
}
```

**Response**

```json
{
  "job": {
    "id": "job_20260928213040_9b27e1",
    "type": "index.import",
    "index": "products",
    "status": "importing",
    "created_at": 1759095040,
    "started_at": 1759095040,
    "finished_at": null,
    "stages": [],
    "error": null,
    "result": null
  }
}
```

**SDK Code**

```python Python REST
import json
import requests
import uuid

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

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
    "Idempotency-Key": str(uuid.uuid4()),
}

response = requests.post(
    f"{BASE_URL}/v1/indexes/{INDEX}/import/algolia",
    headers=headers,
    json={
        "app_id": "YOUR_ALGOLIA_APP_ID",
        "api_key": "YOUR_ALGOLIA_READ_KEY",
        "index_name": "prod_products",
        "object_search": True,
    },
    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 index = "products";

const response = await fetch(`${BASE_URL}/v1/indexes/${index}/import/algolia`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    app_id: "YOUR_ALGOLIA_APP_ID",
    api_key: "YOUR_ALGOLIA_READ_KEY",
    index_name: "prod_products",
    object_search: true,
  }),
});

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