Batch ingestion
The Batch API loads large historical datasets — backfills, document collections, archived conversations, migrations from another system — into your Context Graphs. It is the fastest transport for bulk ingestion, and it does not compete with the live graph.add and thread.add_messages traffic serving your agents.
zep-ingest is the recommended wrapper around the Batch API: it handles preparation, ordering, and monitoring on top. Use the Batch API directly when you want to manage batches yourself or already have ingestion code.
Why use the Batch API
Calling graph.add or thread.add_messages once per item works for live data but becomes hard to manage at scale. Compared to issuing those calls one at a time, the Batch API gives you:
- Faster processing. It ingests large datasets faster than the same operations sent one at a time.
- No interference with live traffic. It is designed not to slow the real-time
graph.addandthread.add_messagesingestion serving your agents, so a large backfill can run alongside production. - Progress you can watch. Monitor each batch’s status, item counts, and errors in the batch dashboard, or poll programmatically.
- One batch instead of many calls. Group items into a single batch — splitting across batches when needed (see Batch limits) — and hand it off to Zep to process as one job.
Backfills
For historical imports, zep-ingest is the recommended path: it prepares sources, preserves timestamps and order, submits through the Batch API when available, and monitors completion. Use the Batch API directly when you already manage batches yourself.
Within one graph: submit all episodes without polling between adds. See Submit many episodes, poll once for ingestion order and what to poll on each path.
Across multiple graphs: you do not need to wait for one graph to finish extraction before submitting to another. Create every destination and set ontology first — ontology is not retroactive — then submit episodes to every graph. After all submits are queued, wait once per graph when you need the data to be searchable. Extraction runs share account-level concurrency limits, so a very large parallel backfill will not run every graph at full speed simultaneously. zep-ingest handles one destination per call and does not parallelize across graphs for you; run separate imports concurrently when you backfill several graphs at once.
How batches work
A batch follows a three-step lifecycle:
Items in a batch are grouped by destination graph and processed in the order they were added. Episodes and messages added through the Batch API are priced the same as those added through graph.add or thread.add_messages.
Batch limits
- A single batch can contain up to 50,000 items.
- Each call to
batch.addaccepts up to 350 items.
To ingest more than 350 items, make multiple batch.add calls against the same batch ID before calling batch.process.
Strict ontology
Set strict_ontology once on batch.create. Processing copies that value onto every graph episode and every thread message in the batch. You cannot set the flag per item.
See Setting Strict Ontology for what the flag does.
Quickstart
The example below creates a batch, adds a mix of graph episodes and thread messages, starts processing, and polls until the batch finishes.
Adding items to a batch
Each item in a batch is one of two types:
graph_episode— equivalent to a singlegraph.addcall. Targets a graph bygraph_idor a user graph byuser_id.thread_message— equivalent to one message inside athread.add_messagescall. Targets a thread bythread_id.
The fields below mirror the equivalent fields on graph.add and thread.add_messages. See Adding business data and Adding messages for the underlying semantics.
A batch item is a single SDK type covering both kinds, so every field is settable on every item. Fields that do not apply to an item’s type are still validated, but not stored — source_description on a thread_message is rejected above 500 characters, and a valid value is discarded.
Common fields
Graph episode fields (type: "graph_episode")
Thread message fields (type: "thread_message")
Setting timestamps on batch items
Pass created_at on each item to give Zep accurate temporal information for historical data. This is important for backfills — Zep uses these timestamps in its fact invalidation process to determine the valid_at and invalid_at values on extracted facts (edges).
The created_at value should be in RFC3339 format (e.g., "2024-06-15T10:30:00Z").
Both item types honor the value you supply: a thread_message item’s created_at dates the episode Zep extracts from that message, exactly as it does for a graph_episode item, so a batch backfill and a direct thread.add_messages call place the same message at the same point in the timeline. An item without a created_at is dated at ingestion time.
Tracking progress
Two methods report on a running or completed batch:
batch.get(batch_id)returns a summary of the whole batch, including aprogressobject with counts fortotal_items,queued_items,processing_items,succeeded_items,failed_items,skipped_items,canceled_items, andpercent_complete. Beforebatch.processis called the batch is indraftand theprogresscounts are unpopulated; once processing starts the counts begin to update.batch.list_items(batch_id)returns each item with its individual status (pending,queued,processing,succeeded,failed,skipped,canceled).
When polling batch.get, a few-second interval (e.g., 5 seconds) is appropriate for small batches. For batches with thousands of items or more, polling becomes impractical — subscribe to the ingest.batch.completed webhook instead to be notified when a batch reaches a terminal state. The payload includes the batch_id so you can match it back to the batch you submitted.
Batch statuses
The status field on BatchSummary is one of:
Once a batch reaches succeeded, partial, failed, or canceled, no further state changes occur. invalid is also non-progressing — the batch never starts processing, but the state persists until you delete the batch. When polling, exit on any of succeeded, partial, failed, canceled, or invalid.
Per-item statuses
The status field on each BatchItemDetail is one of:
Listing and managing batches
Use batch.list to enumerate batches in your project, optionally filtered by status. Use batch.delete to remove a batch that has not yet been processed — once a batch has been processed, it cannot be deleted.
Viewing batches in the dashboard
The Zep web dashboard provides a batches view showing all batches in your project, their status, item counts, and processing progress. Click into a batch to inspect its individual items and any errors. You can delete a draft or invalid batch from the list or the batch detail page. Batches that have started processing cannot be deleted.
Deprecated batch methods
The following methods are deprecated and no longer recommended. Use the Batch API described above for all new ingestion work.
The deprecated methods continue to work but will be removed in a future release.