Query threads using the SDK

no
Summary: Programmatically fetch and inspect multi-turn conversation threads from your LangSmith projects.

Original Documentation

Documentation Index#

Fetch the complete documentation index at: https://docs.langchain.com/llms.txt Use this file to discover all available pages before exploring further.

Programmatically fetch and inspect multi-turn conversation threads from your LangSmith projects.

If you’re building a conversational agent or any multi-turn application, LangSmith automatically groups your runs into threads. Querying threads lets you replay full conversations, audit agent behavior across sessions, build analytics on conversation length and latency, and feed downstream workflows like fine-tuning and evaluation.

The SDK exposes two methods for working with threads:

MethodUse when
list_threads / listThreadsYou want to browse all threads in a project
read_thread / readThreadYou already know the thread ID and need its runs

How threads work#

Each run you create can carry a thread_id in its metadata. LangSmith uses this to group runs into threads. The backend looks for thread_id in metadata (falling back to session_id or conversation_id).

If you’re using a tracing integration, pass thread_id in the run metadata:

from langsmith import traceable

@traceable(metadata={"thread_id": "conv-abc123"})
def my_agent(user_message: str) -> str:
    ...
import { traceable } from "langsmith/traceable";

const myAgent = traceable(
  async (userMessage: string) => {
    // ...
  },
  { metadata: { thread_id: "conv-abc123" } }
);

List all threads in a project#

list_threads / listThreads fetches all threads in a project and groups their runs together. Results are sorted by most recent activity first.

from langsmith import Client

client = Client()

threads = client.list_threads(project_name="my-project")

for thread in threads:
    print(thread["thread_id"])
    print(f"  {thread['count']} runs")
    print(f"  last active: {thread['max_start_time']}")
import { Client } from "langsmith";

const client = new Client();

const threads = await client.listThreads({ projectName: "my-project" });

for (const thread of threads) {
  console.log(thread.thread_id);
  console.log(`  ${thread.count} runs`);
  console.log(`  last active: ${thread.max_start_time}`);
}

Results are sorted by most recent activity:

conv-abc123
  3 runs
  last active: 2026-02-25T10:05:42+00:00
conv-def456
  1 runs
  last active: 2026-02-25T09:30:00+00:00

Parameters#

ParameterTypeDefaultDescription
project_name / projectNamestringProject name. Required if project_id is not set.
project_id / projectIdstringProject ID. Required if project_name is not set.
limitintallMaximum number of threads to return.
offsetint0Number of threads to skip (for pagination).
filterstringFilter expression applied when fetching runs, using LangSmith trace query syntax.
start_time / startTimedatetime / Date1 day agoOnly include runs started after this time. Widen this window to surface older threads.

Return value#

A list of thread objects, each containing:

FieldTypeDescription
thread_idstringThe thread identifier.
runs[Run](https://reference.langchain.com/python/langsmith/schemas/Run)[]Root runs in this thread, sorted chronologically (oldest first).
countintNumber of runs in this thread.
min_start_timestring | nullISO timestamp of the earliest run.
max_start_timestring | nullISO timestamp of the most recent run.

list_threads always returns root runs only. If you need child runs (e.g., tool calls, sub-chains), use read_thread instead, which accepts an is_root / isRoot parameter you can set to false.

Read runs for a single thread#

When you already know the thread_id, use read_thread / readThread. It returns an iterator over the thread’s runs directly, without fetching all threads first.

from langsmith import Client

client = Client()

for run in client.read_thread(
    thread_id="conv-abc123",
    project_name="my-project",
):
    print(run.id, run.name, run.start_time)
import { Client } from "langsmith";

const client = new Client();

for await (const run of client.readThread({
  threadId: "conv-abc123",
  projectName: "my-project",
})) {
  console.log(run.id, run.name, run.start_time);
}

Unlike list_threads, each item here is a Run object directly — there is no grouping wrapper. Runs are returned in ascending chronological order by default.

[
    Run(id=UUID("a1b2..."), name="my_agent", run_type="chain", status="success", start_time=datetime(2026, 2, 25, 10, 0, 0, tzinfo=utc), ...),
    Run(id=UUID("c3d4..."), name="my_agent", run_type="chain", status="success", start_time=datetime(2026, 2, 25, 10, 3, 11, tzinfo=utc), ...),
    Run(id=UUID("e5f6..."), name="my_agent", run_type="chain", status="error",   start_time=datetime(2026, 2, 25, 10, 5, 42, tzinfo=utc), ...),
]

Parameters#

ParameterTypeDefaultDescription
thread_id / threadIdstringRequired. The thread to query.
project_name / projectNamestringProject name. Required if project_id is not set.
project_id / projectIdstring | string[]Project ID or list of IDs. Required if project_name is not set.
is_root / isRootbooltrueReturn only root runs. Set to false to include child runs.
limitintallMaximum number of runs to return.
filterstringAdditional filter expression (combined with the thread filter).
order"asc" | "desc""asc"Sort order. "asc" returns runs oldest-first (chronological).
selectstring[]all fieldsSpecific run fields to return, to reduce response size.

Return value#

An iterator (Python) or async iterator (TypeScript) of Run objects.

Examples#

Filter threads by run properties#

Pass a filter expression to narrow results using LangSmith trace query syntax. For example, to surface only threads containing at least one failed run:

threads = client.list_threads(
    project_name="my-project",
    filter='eq(status, "error")',
)
const threads = await client.listThreads({
  projectName: "my-project",
  filter: 'eq(status, "error")',
});

Look back further than 24 hours#

By default, list_threads only surfaces threads with runs from the last day. Pass start_time to widen the window:

import datetime

threads = client.list_threads(
    project_name="my-project",
    start_time=datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=2),
)
const threads = await client.listThreads({
  projectName: "my-project",
  startTime: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000),
});

Reconstruct a conversation#

Use read_thread with order="asc" to replay a conversation turn by turn:

runs = list(
    client.read_thread(
        thread_id="conv-abc123",
        project_name="my-project",
        order="asc",
    )
)

for run in runs:
    user_msg = run.inputs.get("messages", [{}])[-1].get("content", "")
    assistant_msg = (run.outputs or {}).get("content", "")
    print(f"User:      {user_msg}")
    print(f"Assistant: {assistant_msg}")
    print()
const runs: Run[] = [];
for await (const run of client.readThread({
  threadId: "conv-abc123",
  projectName: "my-project",
  order: "asc",
})) {
  runs.push(run);
}

for (const run of runs) {
  const messages = (run.inputs?.messages ?? []) as Array<Record<string, string>>;
  const userMsg = messages.at(-1)?.content ?? "";
  const assistantMsg = (run.outputs as Record<string, string>)?.content ?? "";
  console.log(`User:      ${userMsg}`);
  console.log(`Assistant: ${assistantMsg}`);
}

Edit this page on GitHub or file an issue.

Connect these docs to Claude, VSCode, and more via MCP for real-time answers.

Link last verified June 7, 2026. View original ↗
Source: LangChain Docs
Link last verified: 2026-04-05