Quick Start Guide

Integrate Zep into your AI application in minutes

Zep delivers agent memory at enterprise scale, giving your AI agents the right context at the right time. From a temporal Context Graph, Zep assembles relevant context from chat history, business data, and user behavior—so agents make better decisions with accurate, up-to-date information. With a simple three-line API and sub-200ms retrieval, Zep helps you build personalized, reliable agents without building a context pipeline.

Get started with the example in the video using:

$git clone https://github.com/getzep/zep.git
$cd zep/examples/python/agent-memory-full-example

This guide shows you how to integrate Zep into your AI application to provide personalized context for every user interaction. You’ll learn how to ingest user messages and business data, then retrieve assembled context that includes user preferences, traits, and relevant facts—all optimized for your LLM’s context window.

Looking for a more in-depth understanding? Check out our Key Concepts page.

Migrating from Mem0? Check out our Mem0 Migration guide.

Install the Zep SDK

Set up your Python project, ideally with a virtual environment, and then:

$pip install zep-cloud

Initialize the Zep client

After creating a Zep account, obtaining an API key, and setting the API key as an environment variable, initialize the client once at application startup and reuse it throughout your application.

1import os
2from zep_cloud.client import Zep
3
4API_KEY = os.environ.get('ZEP_API_KEY')
5
6client = Zep(
7 api_key=API_KEY,
8)

Create a Zep user for each of your users

Whenever users are created in your application, you need to trigger the creation of a Zep user. Make sure to include at least their first name, and ideally also their last name and email to ensure correct identification of the user in future messages. We recommend setting the Zep user ID equal to your internal user ID.

Backfilling existing users: Run a one-time migration that calls user.add for each existing user.

Provide at least the first name and ideally the last name when calling user.add to ensure Zep correctly associates the user with references in your data. If needed, add this information later using the update user method.

1from zep_cloud.client import Zep
2
3client = Zep(api_key=API_KEY)
4
5# You can choose any user ID, but we recommend using your internal user ID
6user_id = "your_internal_user_id"
7
8new_user = client.user.add(
9 user_id=user_id,
10 email="[email protected]",
11 first_name="Jane",
12 last_name="Smith",
13)

Create a Zep thread for each of your threads

Whenever a user starts a new conversation with your agent, you need to trigger the creation of a Zep thread. Learn more about adding messages.

Backfilling prior conversations: For prior conversations, you will need to run a one-time migration to create Zep threads for those conversations and add the prior messages to the respective Zep threads. zep-ingest is the recommended way to do this: it validates each row, creates the threads, preserves message order, and monitors the import. You can also drive the Batch API yourself.

1client = Zep(
2 api_key=API_KEY,
3)
4thread_id = uuid.uuid4().hex # A new thread identifier
5
6client.thread.create(
7 thread_id=thread_id,
8 user_id=user_id,
9)

Add incoming user messages to Zep

When a new user message comes in, add the user message to Zep, providing the user’s name in the message if possible.

It is important to provide the name of the user in the name field if possible, to help with graph construction.

Include the created_at timestamp (RFC3339 format) representing when the message was originally sent. This ensures accurate temporal understanding in the knowledge graph. See Setting message timestamps for more details.

1from zep_cloud.client import Zep
2from zep_cloud.types import Message
3from datetime import datetime, timezone
4
5zep_client = Zep(
6 api_key=API_KEY,
7)
8
9messages = [
10 Message(
11 created_at=datetime.now(timezone.utc).isoformat(),
12 name="Jane Smith",
13 role="user",
14 content="Who was Octavia Butler?",
15 )
16]
17
18response = zep_client.thread.add_messages(thread_id, messages=messages)

Add streaming business data to Zep

Beyond chat messages, you can provide Zep with additional context about your users by sending business data directly to their knowledge graphs. This includes user interactions with your application, transactions, support tickets, emails, transcripts—essentially any information that gives context about the user and can be represented as text.

Use the graph.add method to send structured, semi-structured, or unstructured text data to Zep. Include a reference to the user—their full name, user ID, or both—so Zep can correctly associate the data with the user in their knowledge graph. Read more about adding business data.

Any text can be sent to Zep—structured JSON, semi-structured logs, or plain text descriptions. The example below shows a JSON event, but you could also send "User Jane Smith listened to 'Bohemian Rhapsody' by Queen" as plain text. See Adding business data for more data type options.

1from zep_cloud.client import Zep
2import json
3
4client = Zep(api_key=API_KEY)
5
6# Example: User listened to a song in your application
7event_data = {
8 "user_id": "user123",
9 "user_name": "Jane Smith",
10 "event_type": "song_played",
11 "song_title": "Bohemian Rhapsody",
12 "artist": "Queen",
13 "duration_seconds": 354
14}
15
16client.graph.add(
17 user_id="user123",
18 type="json",
19 data=json.dumps(event_data)
20)

Retrieve a Zep Context Block

Retrieve a Context Block after you add the user message and before the model generates a response. The block contains relevant information from the user’s graph.

The Context Block can contain user or external content. Pass it through your model provider’s untrusted-data channel, as described in Memory security best practices.

Use the default Context Block

Zep assembles the default Context Block with semantic search, full-text search, and graph search. The four most recent messages from the thread form the query.

The Context Block has a latency of less than 200 ms at the 95th percentile.

1# Get context for the thread
2user_context = client.thread.get_user_context(thread_id=thread_id)
3
4# Access the context block for use as untrusted model input
5context_block = user_context.context
6print(context_block)

The Context Block can include all six context types. Smart Context Assembly selects available types that are relevant to the recent messages.

This example contains a user summary and facts:

# This is the user summary
<USER_SUMMARY>
Emily Painter is a user with account ID Emily0e62 who uses digital art tools for creative work. She maintains an active account with the service, though has recently experienced technical issues with the Magic Pen Tool. Emily values reliable payment processing and seeks prompt resolution for account-related issues. She expects clear communication and efficient support when troubleshooting technical problems.
</USER_SUMMARY>
# These are the most relevant facts and their valid date ranges
# format: FACT (Date range: from - to)
<FACTS>
- Emily is experiencing issues with logging in. (2024-11-14 02:13:19+00:00 - present)
- User account Emily0e62 has a suspended status due to payment failure. (2024-11-14 02:03:58+00:00 - present)
- user has the id of Emily0e62 (2024-11-14 02:03:54 - present)
- The failed transaction used a card with last four digits 1234. (2024-09-15 00:00:00+00:00 - present)
- The reason for the transaction failure was 'Card expired'. (2024-09-15 00:00:00+00:00 - present)
- user has the name of Emily Painter (2024-11-14 02:03:54 - present)
- Account Emily0e62 made a failed transaction of 99.99. (2024-07-30 00:00:00+00:00 - 2024-08-30 00:00:00+00:00)
</FACTS>

Use a custom Context Block

Use a context template to control the Context Block format. Pass the template ID to thread.get_user_context().

Create a context template

Create a context template for your Zep project and save the template ID. The context templates guide lists the supported syntax and variables.

1from zep_cloud import Zep
2
3client = Zep(api_key="YOUR_API_KEY")
4
5client.context.create_context_template(
6 template_id="customer-support",
7 template="""# CUSTOMER PROFILE
8%{user_summary}
9
10# FACTS
11%{edges limit=10}
12
13# KEY ENTITIES
14%{entities limit=5}"""
15)

Retrieve custom context block using thread.get_user_context()

Retrieve your custom context block using the thread.get_user_context() method, passing in your template ID.

1from zep_cloud import Zep
2
3client = Zep(api_key="YOUR_API_KEY")
4
5user_context = client.thread.get_user_context(
6 thread_id="thread_id",
7 template_id="customer-support"
8)
9context_block = user_context.context

Add context block to agent context window

As outlined in our retrieval philosophy, Zep optimizes for high recall over precision, meaning we err on the side of including more results even if some are less relevant. Most agents will automatically reference only the most relevant information when responding to the user message.

The Context Block can contain text that came from end users, documents, tools, or other external sources. A privileged message gives that text higher instruction priority than ordinary input. Keep the Context Block out of system messages, developer messages, and other privileged instruction channels.

Follow your model provider’s documented method for separating instructions from data:

  • For the OpenAI Responses API, send preloaded context through ordinary input or a user message. Use function_call_output only for the result of an actual function call.
  • For the Anthropic Messages API, design retrieval as a tool call when context can contain third-party data. Return the context in a tool_result block linked to the original tool_use_id.
  • For other providers, use the documented untrusted-data channel. If the provider does not define one, use an ordinary user-level message with explicit data framing.

OpenAI with preloaded context

Message typeContent
Developer or instructionsStable application policy. No Zep context.
AssistantAn assistant message stored in Zep
UserA user message stored in Zep
User or ordinary input{Zep Context Block} framed as reference data
UserThe latest user request

Place the Context Block after the conversation history and before the latest user request. Everything before the block stays unchanged between turns, so this order preserves the cacheable prefix that prompt caching needs. Replace the previous turn’s block instead of appending a second one.

If the model requests memory through a function, return the Context Block as function_call_output linked to the original call_id.

OpenAI Chat Completions with tool-retrieved context

Message typeContent
DeveloperStable application policy. No Zep context.
UserThe latest user request
AssistantA tool call requesting Zep retrieval
Tool{Zep Context Block}, linked by tool_call_id
AssistantThe response to the user

Anthropic with tool-retrieved context

Message typeContent
SystemStable application policy. No Zep context.
UserThe latest user request
AssistantA tool_use block requesting Zep retrieval
UserA tool_result block with {Zep Context Block}, linked by tool_use_id
AssistantThe response to the user

Do not create a tool message for preloaded context unless the provider documents that pattern. A tool-result type must remain linked to the model’s actual tool request.

Read Memory security best practices for provider-specific mappings, write controls, action authorization, and recovery guidance.

Add assistant response to Zep

After generating the assistant response, add it to Zep to continue building the user’s knowledge graph.

1from zep_cloud.types import Message
2from datetime import datetime, timezone
3
4messages = [
5 Message(
6 created_at=datetime.now(timezone.utc).isoformat(),
7 name="AI Assistant",
8 role="assistant",
9 content="Octavia Butler was an influential American science fiction writer...",
10 )
11]
12
13response = zep_client.thread.add_messages(thread_id, messages=messages)

Next steps

Now that you’ve integrated Zep into your application, you can explore additional features: