For AI agents: this page is also available as Markdown at /docs/ingesting-documents.md. Every docs page is: add .md to its URL. llms.txt lists them all.
Ingesting documents
You get documents into a collection by posting JSON to it. You can use the native REST API shown
here, the Elasticsearch-compatible _bulk API, Solr's update API, the MCP index_documents tool,
or the Ingest tab of a collection in the console. The collection must already
exist; see Collections.
#Document shape
A document is a flat JSON object. Give every document an id so you can replace or delete it later.
{
"id": "b1",
"title": "The Way of Kings",
"genre": "fantasy",
"year": 2010,
"tags": ["epic", "fantasy"],
"location": [-74.006, 40.7128],
"embedding": [0.12, -0.08, 0.31]
}
- Field names must be mapped or end in a type suffix such as
_t,_sor_i. Anything else is rejected for that document. See Schemas and field types. - Arrays hold multiple values for one field. A multi-valued geo field is an array of
[lon, lat]pairs. - Geo points are
[longitude, latitude], in that order. - Send vectors yourself, as arrays of numbers with the collection's
dims. WarpSearch does not create embeddings from text. See Vector search. - Nested objects and arrays of objects are rejected. Flatten them first.
- Elasticsearch-style names with
@or.work as long as you mapped them; see Schemas and field types.
Not supported: automatic embedding on ingest. Compute embeddings in your pipeline and send them in the document.
#Send documents
POST /v1/collections/{c}/documents takes a JSON array, an object with a docs array, or NDJSON
with one document per line.
curl -X POST "https://api.warpsearch.io/v1/collections/books/documents?commit=true" \
-H "Authorization: Bearer $WARPSEARCH_API_KEY" \
-H "Content-Type: application/json" \
-d '[
{"id": "b1", "title": "Dune", "genre": "scifi", "year": 1965},
{"id": "b2", "title": "Dune Messiah", "genre": "scifi", "year": 1969}
]'
Options can also go inside an object body:
{"docs": [{"id": "b1", "title": "Dune"}], "commit": true, "all_or_none": false}
For a file you already have, send it as NDJSON and let curl pass the bytes through untouched:
curl -X POST "https://api.warpsearch.io/v1/collections/books/documents" \
-H "Authorization: Bearer $WARPSEARCH_API_KEY" \
-H "Content-Type: application/x-ndjson" \
--data-binary @books.ndjson
| Option | Meaning |
|---|---|
commit=true |
Make the documents searchable before responding. See Writes, visibility and durability. |
all_or_none=true |
Apply the whole request or nothing: one bad document rolls back the rest. The request must fit in a single backend update, so keep these batches small. |
The response reports the outcome:
{"status": "ok", "indexed": 2, "failed": 0, "took_ms": 14}
status is ok (everything written), partial (some failed) or error (nothing written).
#Replace a document
Posting a document with an id that exists replaces the whole document. Fields you leave out are
gone afterwards. There are no partial updates, and the same call both creates and replaces.
Not supported: partial or field-level updates, and delete by query. To change one field, read the document, change it and write it back in full.
#Get a document
curl https://api.warpsearch.io/v1/collections/books/documents/b1 \
-H "Authorization: Bearer $WARPSEARCH_API_KEY"
This returns {"id": ..., "fields": {...}}. A document is readable once committed, not in real
time, and it needs only the viewer role.
#Delete documents
# One document
curl -X DELETE "https://api.warpsearch.io/v1/collections/books/documents/b1?commit=true" \
-H "Authorization: Bearer $WARPSEARCH_API_KEY"
# Many, by id
curl -X POST https://api.warpsearch.io/v1/collections/books/documents/_delete \
-H "Authorization: Bearer $WARPSEARCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ids": ["b1", "b2"], "commit": true}'
Deleting an id that does not exist succeeds. Deleting needs the editor role.
#Partial failures
A write can succeed for most documents and fail for a few, and that is still a normal HTTP response. Always read the body. The failures are listed with the position and id of each:
{
"status": "partial",
"indexed": 1,
"failed": 1,
"errors": [
{"index": 1, "id": "b2", "code": "unknown_field", "message": "Field not found: nosuffix"}
],
"took_ms": 9
}
A failed document leaves any older version with the same id untouched. Fix the documents named in
errors and send just those again.
Large requests are split into several backend updates. If one fails after the first has been
applied, the response is 207 and lists every remaining document as failed, with a warning that
they were not written (and not committed, if you asked for a commit). Retry exactly those
documents. Because writes replace by id, retrying is safe.
Other failures are not per document and are explained in Limits and errors:
| Status | Code | Meaning |
|---|---|---|
400 |
invalid_request |
Malformed body, or no documents. |
403 |
forbidden |
Your key has the viewer role, and writing needs editor. |
404 |
not_found |
The collection does not exist, or your key has no role on it. |
413 |
request_too_large |
Over your plan's documents or bytes per request. Send smaller batches. |
429 |
rate_limited, resource_exhausted |
Slow down and retry after Retry-After. |
503 |
warming_up |
The collection is loading from idle. Retry after Retry-After. |
#Batching and throughput
- Batch your writes. Every write request bills a minimum size, so thousands of one-document requests cost far more than a few large ones. See Pricing for the current guidance on batch size.
- Send batches from a few parallel workers, and honor
Retry-Afterwhen you are throttled. - Do not use
commit=trueon every batch of a bulk load. Commit on the last batch, or let WarpSearch commit on its own. - Large documents (several KB each) produce many small segments on a big load; a single
_forcemergethrough the Elasticsearch-compatible API after the load keeps refreshes fast. - Your plan sets the maximum documents and bytes per request. The limits are in the console and are described in Limits and errors.
#Other ways in
- Elasticsearch and OpenSearch. Use
_bulkor the single-document_docAPIs at/compat/esor/compat/opensearch. Unknown indices are created on the fly when your key is allowed to create collections.updateactions are full replacements; partial updates are per-item errors. See Elasticsearch compatibility and OpenSearch compatibility. - Solr.
/compat/solr/{collection}/updateaccepts JSON documents andadd,deleteby id andcommitcommands. Delete by query and atomic updates are not supported. See Solr compatibility. - AI agents. The MCP
index_documentsanddelete_documentstools take the same shapes, withcommit: truefor immediate searchability. See Guide for AI agents.
#Checklist
- Create the collection and its mapping first.
- Use stable ids so loads can be repeated.
- Send batches, read every response body, retry only the failed documents.
- Commit on the last batch, then confirm with a count:
curl -X POST https://api.warpsearch.io/v1/collections/books/count \
-H "Authorization: Bearer $WARPSEARCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"language": "lucene", "query": "*:*"}'