jsontoolskit.org
JSON and JSON Schema utilities, in the browser
Say hi →

JSON to Elasticsearch Bulk

_bulk NDJSON · index / create / update · ids, routing, pipeline

The _bulk API does not take a JSON array. It takes newline-delimited pairs — an action line, then the document — and it is strict about every part of that: no pretty-printing, no missing trailing newline. This builds the body from a plain array, pulls the document id out of a field you name, and adds routing or an ingest pipeline when you need them.

Paste input to start.

How to build a bulk request

  1. Paste a JSON array of documents. A single object works too and produces one pair.
  2. Name the index. It goes into every action line as _index, so one body can only target one index here.
  3. Name the id field if your documents carry their own identifier — without it the cluster generates one, and re-running the import creates duplicates rather than replacing.
  4. Download and send it: curl -H 'Content-Type: application/x-ndjson' --data-binary @bulk.ndjson http://localhost:9200/_bulk. The --data-binary matters — plain -d strips newlines and the request fails.

Which action to use

index creates the document or replaces it entirely — idempotent as long as the ids are stable, and the right default for a re-runnable import. create fails the individual item if the id already exists, which is how you detect accidental re-imports rather than silently overwriting. update emits {"doc": …, "doc_as_upsert": true}, merging your fields into whatever is already there and creating the document if it is absent — use it when your array holds partial records and the index has fields you must not clobber.

The format rules that bite

Every line must be on one line. A pretty-printed document breaks the parser, because the API splits on newlines to find the next action. The output here is always compact. The body must end with a newline — a missing final newline is the single most common cause of a bulk request being rejected, and it is easy to lose when copying by hand rather than downloading. Partial failure is normal. The response has a errors flag and a per-item status; a 200 does not mean every document indexed. Always check the item statuses.

Batch size is the other practical limit. Somewhere between 5 and 15 MB per request is the usual sweet spot — split larger files rather than sending one enormous body. This format is identical for OpenSearch, which forked from Elasticsearch and kept the bulk API unchanged.

Privacy

Nothing is uploaded. The conversion runs in this tab using your own browser's JavaScript engine — no server sees your data, and this page never contacts your cluster.

FAQ

Why is the id also inside the document?

Because most mappings want it queryable. Tick remove the id field if you would rather rely on the metadata id alone — Elasticsearch stores _id either way, but it is not part of _source unless you keep it.

Can I target several indices in one body?

The API allows it, this page does not — one index goes into every action line. For a multi-index import, split the array by target with a filter and run one pass per index, then concatenate the outputs.

What about delete actions?

Delete lines have no document body, so they do not fit the pair-per-record shape here. They are a single line each — {"delete":{"_index":"i","_id":"1"}} — and are easy to generate from a list of ids with a template.

Is this the same as NDJSON generally?

The file format is, and JSON to NDJSON produces plain one-object-per-line output. Bulk adds the interleaved action metadata lines, which is what makes it a request rather than a data file.