> ## Documentation Index
> Fetch the complete documentation index at: https://langchain-5e9cc07a-preview-docsby-1791319236-3be7a15.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate thread methods to SmithDB

> Migrate the LangSmith SDK thread methods to their SmithDB-backed equivalents.

These methods query threads and list the traces inside a thread. For deprecation dates, minimum SDK versions, and the agent prompt that applies to every method, see [Migrate to SmithDB-backed SDK methods](/langsmith/smithdb-sdk-migration).

## Threads: query

Query threads within a project, with cursor-based pagination. Returns threads matching the given time range and optional filter.

### Main changes

#### Method name

<Tabs>
  <Tab title="Python">
    | Before | After |
    | - | - |
    | `client.list_threads()` | `client.threads.query()` |

    <Note>
      `client.threads.query()` is now async. Call it with `await`.
    </Note>

    See the [reference](https://reference.langchain.com/python/langsmith/_openapi_client/resources/threads/ThreadsResource/query) for the full parameter and field list.
  </Tab>

  <Tab title="TypeScript">
    | Before | After |
    | - | - |
    | `client.listThreads()` | `client.threads.query()` |

    See the [reference](https://reference.langchain.com/javascript/langsmith/_openapi_client/Langsmith/Threads/query) for the full parameter and field list.
  </Tab>

  <Tab title="Java">
    <Note>Java never had a dedicated thread-listing method. The closest legacy equivalent is the generic run query, manually grouped by the `thread_id` metadata convention.</Note>

    | Before | After |
    | - | - |
    | `client.runs().query()` (generic, grouped client-side) | `client.threads().query()` |

    See the [reference](https://javadoc.io/doc/com.langchain.smith/langsmith-java/latest/com/langchain/smith/services/blocking/ThreadService.html) for the full parameter list.
  </Tab>

  <Tab title="Go">
    <Note>Go never had a dedicated thread-listing method. The closest legacy equivalent is the generic run query, manually grouped by the `thread_id` metadata convention.</Note>

    | Before | After |
    | - | - |
    | `client.Runs.Query()` (generic, grouped client-side) | `client.Threads.Query()` |

    See the [reference](https://pkg.go.dev/github.com/langchain-ai/langsmith-go#ThreadService.QueryAutoPaging) for the full parameter list.
  </Tab>

  <Tab title="cURL">
    | Before | After |
    | - | - |
    | `POST /api/v1/runs/query` (`is_root=true`, grouped client-side) | `POST /api/v2/threads/query` |

    See the [API doc](/langsmith/smith-api/threads/query-threads) for the full parameter and field list.
  </Tab>
</Tabs>

#### Query parameters

<Tabs>
  <Tab title="Python">
    | Before (`list_threads`) | After (`threads.query`) | Notes |
    | - | - | - |
    | `project_id` XOR `project_name` | `project_id` | the new method takes only the UUID; resolve a name via `aread_project()` first, same pattern as `Runs: query` |
    | `start_time` (defaults to 1 day ago) | `min_start_time` + `max_start_time` | Optional; default to a 1-day window ending now, same as `start_time` |
    | `offset` + `limit` | `cursor` + `page_size` | Offset pagination replaced by cursor pagination |
    | `filter` (evaluated against runs) | `filter` | Same syntax; now evaluated against each thread's root run |
  </Tab>

  <Tab title="TypeScript">
    | Before (`listThreads`) | After (`threads.query`) | Notes |
    | - | - | - |
    | `projectId` XOR `projectName` | `project_id` | the new method takes only the UUID; resolve a name via `readProject()` first |
    | `startTime` (defaults to 1 day ago) | `min_start_time` + `max_start_time` | Optional; default to a 1-day window ending now, same as `startTime` |
    | `offset` + `limit` | `cursor` + `page_size` | Offset pagination replaced by cursor pagination |
    | `filter` | `filter` | Same syntax; now evaluated against each thread's root run |
  </Tab>

  <Tab title="Java">
    No query parameters to map. There was no dedicated method. The old approach used the generic run query (`is_root=true`, manual grouping by `thread_id` metadata). `threads().query()` takes `projectId`, `minStartTime`, `maxStartTime` (both optional, defaulting to a 1-day window ending now), `filter`, `pageSize`, `cursor`.
  </Tab>

  <Tab title="Go">
    No query parameters to map. There was no dedicated method. The old approach used the generic run query (`IsRoot: true`, manual grouping by `thread_id` metadata). `Threads.Query()` takes `ProjectID`, `MinStartTime`, `MaxStartTime` (both optional, defaulting to a 1-day window ending now), `Filter`, `PageSize`, `Cursor`.
  </Tab>

  <Tab title="cURL">
    `POST /api/v2/threads/query` body fields: `project_id`, `min_start_time` (optional), `max_start_time` (optional), `filter`, `page_size`, `cursor` (all `snake_case`). `min_start_time`/`max_start_time` default to a 1-day window ending now when omitted.
  </Tab>
</Tabs>

#### Response fields

<Tabs>
  <Tab title="Python">
    Python's legacy `ListThreadsItem` only has `thread_id`, `runs` (full embedded `Run[]`), `count`, `min_start_time`, `max_start_time`. It has no token/cost/latency/feedback fields at all.

    The new `Thread` never embeds the full run list (that is what `threads.list_traces` is for) but adds real `feedback_stats`, `latency_p50`/`latency_p99`, cost/token sums with per-category `_details`, `first_trace_id`/`last_trace_id`, `first_inputs`/`last_outputs` previews, `last_error`, `num_errored_turns`.

    | Before (legacy `ListThreadsItem`) | After (new `Thread`) | Notes |
    | - | - | - |
    | `thread_id` | `thread_id` | Unchanged |
    | `runs` (full embedded `Run[]`) | *(not available)* | Use `threads.list_traces` for per-trace detail |
    | `count` | `count` | Unchanged |
    | `min_start_time` | `min_start_time` | Unchanged |
    | `max_start_time` | `max_start_time` | Unchanged |
    | *(not available)* | `start_time` | New: a reference start time for this row, for example for sorting |
    | *(not available)* | `trace_id` | New: a representative root trace UUID, for example for deep links |
    | *(not available)* | `first_trace_id`, `last_trace_id` | New: chronologically first/last trace UUID in the query window |
    | *(not available)* | `first_inputs`, `last_outputs` | New: truncated previews from the first/last trace |
    | *(not available)* | `last_error` | New |
    | *(not available)* | `num_errored_turns` | New |
    | *(not available)* | `latency_p50`, `latency_p99` | New |
    | *(not available)* | `total_tokens`, `total_cost` | New |
    | *(not available)* | `total_token_details`, `total_cost_details` | New: per-category dicts, unlike `threads.list_traces` these are not wrapped in `.raw` |
    | *(not available)* | `feedback_stats` | New |
  </Tab>

  <Tab title="TypeScript">
    | Before (legacy `ListThreadsItem`) | After (new `Thread`) | Notes |
    | - | - | - |
    | `thread_id` | `thread_id` | Unchanged |
    | `runs` (full embedded `Run[]`) | *(not available)* | Use `threads.listTraces` for per-trace detail |
    | `count` | `count` | Unchanged |
    | `min_start_time` | `min_start_time` | Unchanged |
    | `max_start_time` | `max_start_time` | Unchanged |
    | `total_tokens` | `total_tokens` | Unchanged |
    | `total_cost` | `total_cost` | Unchanged |
    | `latency_p50`, `latency_p99` | `latency_p50`, `latency_p99` | Unchanged |
    | `feedback_stats` | `feedback_stats` | Unchanged |
    | `first_inputs`, `last_outputs` | `first_inputs`, `last_outputs` | Unchanged |
    | `last_error` | `last_error` | Unchanged |
    | *(not available)* | `start_time` | New: a reference start time for this row, for example for sorting |
    | *(not available)* | `trace_id` | New: a representative root trace UUID, for example for deep links |
    | *(not available)* | `first_trace_id`, `last_trace_id` | New: chronologically first/last trace UUID in the query window |
    | *(not available)* | `num_errored_turns` | New |
    | *(not available)* | `total_token_details`, `total_cost_details` | New: per-category dicts, unlike `threads.listTraces` these are not wrapped in `.raw` |
  </Tab>

  <Tab title="Java">
    `Thread` has 19 fields: `threadId`, `count`, `feedbackStats`, `firstInputs`, `firstTraceId`, `lastError`, `lastOutputs`, `lastTraceId`, `latencyP50`, `latencyP99`, `maxStartTime`, `minStartTime`, `numErroredTurns`, `startTime`, `totalCost`, `totalCostDetails`, `totalTokenDetails`, `totalTokens`, `traceId` (all `Optional`).

    The legacy SDK never had a typed response for this. Java's closest equivalent grouped raw `runs().query()` results by the `thread_id` metadata client-side. Every field below is new.

    | New `Thread` method | Notes |
    | - | - |
    | `threadId()` | |
    | `count()` | |
    | `minStartTime()`, `maxStartTime()`, `startTime()` | |
    | `firstTraceId()`, `lastTraceId()`, `traceId()` | `traceId()` is a representative root trace UUID, for example for deep links, in addition to the first/last trace UUIDs |
    | `firstInputs()`, `lastOutputs()` | Truncated previews from the first/last trace |
    | `lastError()` | |
    | `numErroredTurns()` | |
    | `latencyP50()`, `latencyP99()` | |
    | `totalTokens()`, `totalCost()` | |
    | `totalTokenDetails()`, `totalCostDetails()` | Per-category maps |
    | `feedbackStats()` | |
  </Tab>

  <Tab title="Go">
    `Thread` has 19 fields, in `PascalCase` Go struct form (e.g. `ThreadID`, `Count`, `LatencyP50`).

    The legacy SDK never had a typed response for this. Go's closest equivalent grouped raw `Runs.Query()` results by the `thread_id` metadata client-side. Every field below is new.

    | New `Thread` field | Notes |
    | - | - |
    | `ThreadID` | |
    | `Count` | |
    | `MinStartTime`, `MaxStartTime`, `StartTime` | |
    | `FirstTraceID`, `LastTraceID`, `TraceID` | `TraceID` is a representative root trace UUID, for example for deep links, in addition to the first/last trace UUIDs |
    | `FirstInputs`, `LastOutputs` | Truncated previews from the first/last trace |
    | `LastError` | |
    | `NumErroredTurns` | |
    | `LatencyP50`, `LatencyP99` | |
    | `TotalTokens`, `TotalCost` | |
    | `TotalTokenDetails`, `TotalCostDetails` | Per-category maps |
    | `FeedbackStats` | |
  </Tab>

  <Tab title="cURL">
    JSON response fields use `snake_case`: `thread_id`, `count`, `feedback_stats`, `first_inputs`, `first_trace_id`, `last_error`, `last_outputs`, `last_trace_id`, `latency_p50`, `latency_p99`, `max_start_time`, `min_start_time`, `num_errored_turns`, `start_time`, `total_cost`, `total_cost_details`, `total_token_details`, `total_tokens`, `trace_id`.

    The legacy API never had a dedicated threads endpoint. The closest equivalent was `POST /api/v1/runs/query`, grouped client-side by the `thread_id` metadata. Every field below is new.

    | New `threads.query` response field | Notes |
    | - | - |
    | `thread_id` | |
    | `count` | |
    | `min_start_time`, `max_start_time`, `start_time` | |
    | `first_trace_id`, `last_trace_id`, `trace_id` | `trace_id` is a representative root trace UUID, for example for deep links, in addition to the first/last trace UUIDs |
    | `first_inputs`, `last_outputs` | Truncated previews from the first/last trace |
    | `last_error` | |
    | `num_errored_turns` | |
    | `latency_p50`, `latency_p99` | |
    | `total_tokens`, `total_cost` | |
    | `total_token_details`, `total_cost_details` | Per-category dicts |
    | `feedback_stats` | |
  </Tab>
</Tabs>

### Examples

#### List threads in a project

Fetch every thread with activity in a project during a time range.

<Tabs>
  <Tab title="Python">
    <Tabs sync={false}>
      <Tab title="Before">
        ```python Before theme={null}
        from langsmith import Client

        client = Client()
        threads = client.list_threads(project_name="default")
        for thread in threads:
            print(thread["thread_id"], thread["count"])
        ```
      </Tab>

      <Tab title="After">
        ```python After theme={null}
        import asyncio
        from datetime import datetime, timedelta, timezone

        from langsmith import Client


        async def main():
            client = Client()
            project = await client.aread_project(project_name="default")
            async for thread in client.threads.query(
                project_id=str(project.id),
                min_start_time=datetime.now(timezone.utc) - timedelta(days=30),
                max_start_time=datetime.now(timezone.utc),
            ):
                print(thread.thread_id, thread.count)


        asyncio.run(main())
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="TypeScript">
    <Tabs sync={false}>
      <Tab title="Before">
        ```ts Before theme={null}
        import { Client } from "langsmith";

        const client = new Client();
        const threads = await client.listThreads({ projectName: "default" });
        for (const thread of threads) {
          console.log(thread.thread_id, thread.count);
        }
        ```
      </Tab>

      <Tab title="After">
        ```ts After theme={null}
        import { Client } from "langsmith";

        const client = new Client();
        const project = await client.readProject({ projectName: "default" });
        for await (const thread of client.threads.query({
          project_id: project.id,
          min_start_time: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(),
          max_start_time: new Date().toISOString(),
        })) {
          console.log(thread.thread_id, thread.count);
        }
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Java">
    <Tabs sync={false}>
      <Tab title="Before">
        ```kotlin Before theme={null}
        import com.langchain.smith.client.LangsmithClient
        import com.langchain.smith.client.okhttp.LangsmithOkHttpClient
        import com.langchain.smith.models.runs.RunQueryParams
        import com.langchain.smith.models.sessions.SessionListParams

        val client: LangsmithClient = LangsmithOkHttpClient.fromEnv()

        val project = client.sessions().list(
            SessionListParams.builder().name("default").limit(1L).build()
        ).items().first()

        // v1 has no dedicated thread grouping — the generic run query returns raw
        // root runs, with no built-in way to bucket them by thread.
        val rootRuns = client.runs().query(
            RunQueryParams.builder()
                .addSession(project.id())
                .isRoot(true)
                .build()
        ).runs()
        for (run in rootRuns) {
            println("${run.traceId()} ${run.id()}")
        }
        ```
      </Tab>

      <Tab title="After">
        ```kotlin After theme={null}
        import java.time.OffsetDateTime

        import com.langchain.smith.client.LangsmithClient
        import com.langchain.smith.client.okhttp.LangsmithOkHttpClient
        import com.langchain.smith.models.sessions.SessionListParams
        import com.langchain.smith.models.threads.ThreadQueryParams

        val client: LangsmithClient = LangsmithOkHttpClient.fromEnv()

        val project = client.sessions().list(
            SessionListParams.builder().name("default").limit(1L).build()
        ).items().first()

        val threads = client.threads().query(
            ThreadQueryParams.builder()
                .projectId(project.id())
                .minStartTime(OffsetDateTime.now().minusMonths(1))
                .maxStartTime(OffsetDateTime.now())
                .build()
        ).items()
        for (thread in threads) {
            println("${thread.threadId().get()} ${thread.count().get()}")
        }
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Go">
    <Tabs sync={false}>
      <Tab title="Before">
        ```go Before theme={null}
        package main

        import (
        	"context"
        	"fmt"

        	"github.com/langchain-ai/langsmith-go"
        )

        func main() {
        	ctx := context.Background()
        	client := langsmith.NewClient()

        	sessions, err := client.Sessions.List(ctx, langsmith.SessionListParams{
        		Name:  langsmith.F("default"),
        		Limit: langsmith.F(int64(1)),
        	})
        	if err != nil {
        		panic(err.Error())
        	}
        	projectID := sessions.Items[0].ID

        	runs, err := client.Runs.Query(ctx, langsmith.RunQueryParams{
        		Session: langsmith.F([]string{projectID}),
        		IsRoot:  langsmith.F(true),
        	})
        	if err != nil {
        		panic(err.Error())
        	}

        	threads := map[string]int{}
        	for _, run := range runs.Runs {
        		metadata, ok := run.Extra["metadata"].(map[string]interface{})
        		if !ok {
        			continue
        		}
        		threadID, ok := metadata["thread_id"].(string)
        		if ok {
        			threads[threadID]++
        		}
        	}
        	for threadID, count := range threads {
        		fmt.Println(threadID, count)
        	}
        }
        ```
      </Tab>

      <Tab title="After">
        ```go After theme={null}
        package main

        import (
        	"context"
        	"fmt"
        	"time"

        	"github.com/langchain-ai/langsmith-go"
        )

        func main() {
        	ctx := context.Background()
        	client := langsmith.NewClient()

        	sessions, err := client.Sessions.List(ctx, langsmith.SessionListParams{
        		Name:  langsmith.F("default"),
        		Limit: langsmith.F(int64(1)),
        	})
        	if err != nil {
        		panic(err.Error())
        	}
        	projectID := sessions.Items[0].ID

        	maxStart := time.Now().UTC()
        	minStart := maxStart.AddDate(0, -1, 0)

        	iter := client.Threads.QueryAutoPaging(ctx, langsmith.ThreadQueryParams{
        		ProjectID:    langsmith.F(projectID),
        		MinStartTime: langsmith.F(minStart),
        		MaxStartTime: langsmith.F(maxStart),
        	})
        	for iter.Next() {
        		thread := iter.Current()
        		fmt.Println(thread.ThreadID, thread.Count)
        	}
        	if err := iter.Err(); err != nil {
        		panic(err.Error())
        	}
        }
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="cURL">
    <Tabs sync={false}>
      <Tab title="Before">
        ```bash theme={null}
        PROJECT_ID=$(curl -s "https://api.smith.langchain.com/api/v1/sessions?name=default&limit=1" \
          -H "x-api-key: $LANGSMITH_API_KEY" | jq -r '.[0].id')

        curl -s -X POST "https://api.smith.langchain.com/api/v1/runs/query" \
          -H "x-api-key: $LANGSMITH_API_KEY" \
          -H "Content-Type: application/json" \
          -d "$(jq -n --arg pid "$PROJECT_ID" '{"session": [$pid], "is_root": true}')" \
          | jq '[(.runs // [])[] | select(.extra.metadata.thread_id != null)] | group_by(.extra.metadata.thread_id) | map({
              thread_id: .[0].extra.metadata.thread_id,
              count: length
            })'
        ```
      </Tab>

      <Tab title="After">
        ```bash theme={null}
        PROJECT_ID=$(curl -s "https://api.smith.langchain.com/api/v1/sessions?name=default&limit=1" \
          -H "x-api-key: $LANGSMITH_API_KEY" | jq -r '.[0].id')

        MAX_START=$(date -u +%Y-%m-%dT%H:%M:%SZ)
        MIN_START=$(date -u -d '-1 month' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-1m +%Y-%m-%dT%H:%M:%SZ)
        curl -X POST "https://api.smith.langchain.com/api/v2/threads/query" \
          -H "x-api-key: $LANGSMITH_API_KEY" \
          -H "Content-Type: application/json" \
          -d "$(jq -n --arg pid "$PROJECT_ID" --arg min "$MIN_START" --arg max "$MAX_START" '{
            "project_id": $pid,
            "min_start_time": $min,
            "max_start_time": $max
          }')"
        ```
      </Tab>
    </Tabs>
  </Tab>
</Tabs>

#### Find threads with errors

Find threads that had a turn end in an error.

<Tabs>
  <Tab title="Python">
    <Tabs sync={false}>
      <Tab title="Before">
        ```python Before theme={null}
        from langsmith import Client

        client = Client()
        threads = client.list_threads(project_name="default", filter='eq(status, "error")')
        for thread in threads:
            print(thread["thread_id"])
        ```
      </Tab>

      <Tab title="After">
        ```python After theme={null}
        import asyncio
        from datetime import datetime, timedelta, timezone

        from langsmith import Client


        async def main():
            client = Client()
            project = await client.aread_project(project_name="default")
            async for thread in client.threads.query(
                project_id=str(project.id),
                min_start_time=datetime.now(timezone.utc) - timedelta(days=30),
                max_start_time=datetime.now(timezone.utc),
                filter='eq(status, "error")',
            ):
                print(thread.thread_id, thread.last_error)


        asyncio.run(main())
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="TypeScript">
    <Tabs sync={false}>
      <Tab title="Before">
        ```ts Before theme={null}
        import { Client } from "langsmith";

        const client = new Client();
        const threads = await client.listThreads({
          projectName: "default",
          filter: 'eq(status, "error")',
        });
        for (const thread of threads) {
          console.log(thread.thread_id, thread.last_error);
        }
        ```
      </Tab>

      <Tab title="After">
        ```ts After theme={null}
        import { Client } from "langsmith";

        const client = new Client();
        const project = await client.readProject({ projectName: "default" });
        for await (const thread of client.threads.query({
          project_id: project.id,
          min_start_time: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(),
          max_start_time: new Date().toISOString(),
          filter: 'eq(status, "error")',
        })) {
          console.log(thread.thread_id, thread.last_error);
        }
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Java">
    <Tabs sync={false}>
      <Tab title="Before">
        ```kotlin Before theme={null}
        import com.langchain.smith.client.LangsmithClient
        import com.langchain.smith.client.okhttp.LangsmithOkHttpClient
        import com.langchain.smith.models.runs.RunQueryParams
        import com.langchain.smith.models.sessions.SessionListParams
        import kotlin.jvm.optionals.getOrNull

        val client: LangsmithClient = LangsmithOkHttpClient.fromEnv()

        val project = client.sessions().list(
            SessionListParams.builder().name("default").limit(1L).build()
        ).items().first()

        val rootRuns = client.runs().query(
            RunQueryParams.builder()
                .addSession(project.id())
                .isRoot(true)
                .filter("eq(status, \"error\")")
                .build()
        ).runs()
        for (run in rootRuns) {
            println("${run.traceId()} ${run.error().getOrNull()}")
        }
        ```
      </Tab>

      <Tab title="After">
        ```kotlin After theme={null}
        import java.time.OffsetDateTime

        import com.langchain.smith.client.LangsmithClient
        import com.langchain.smith.client.okhttp.LangsmithOkHttpClient
        import com.langchain.smith.models.sessions.SessionListParams
        import com.langchain.smith.models.threads.ThreadQueryParams
        import kotlin.jvm.optionals.getOrNull

        val client: LangsmithClient = LangsmithOkHttpClient.fromEnv()

        val project = client.sessions().list(
            SessionListParams.builder().name("default").limit(1L).build()
        ).items().first()

        val threads = client.threads().query(
            ThreadQueryParams.builder()
                .projectId(project.id())
                .minStartTime(OffsetDateTime.now().minusMonths(1))
                .maxStartTime(OffsetDateTime.now())
                .filter("eq(status, \"error\")")
                .build()
        ).items()
        for (thread in threads) {
            println("${thread.threadId().get()} ${thread.lastError().getOrNull()}")
        }
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Go">
    <Tabs sync={false}>
      <Tab title="Before">
        ```go Before theme={null}
        package main

        import (
        	"context"
        	"fmt"

        	"github.com/langchain-ai/langsmith-go"
        )

        func main() {
        	ctx := context.Background()
        	client := langsmith.NewClient()

        	sessions, err := client.Sessions.List(ctx, langsmith.SessionListParams{
        		Name:  langsmith.F("default"),
        		Limit: langsmith.F(int64(1)),
        	})
        	if err != nil {
        		panic(err.Error())
        	}
        	projectID := sessions.Items[0].ID

        	runs, err := client.Runs.Query(ctx, langsmith.RunQueryParams{
        		Session: langsmith.F([]string{projectID}),
        		IsRoot:  langsmith.F(true),
        		Filter:  langsmith.F(`eq(status, "error")`),
        	})
        	if err != nil {
        		panic(err.Error())
        	}

        	threadIDs := map[string]bool{}
        	for _, run := range runs.Runs {
        		metadata, ok := run.Extra["metadata"].(map[string]interface{})
        		if !ok {
        			continue
        		}
        		if threadID, ok := metadata["thread_id"].(string); ok {
        			threadIDs[threadID] = true
        		}
        	}
        	for threadID := range threadIDs {
        		fmt.Println(threadID)
        	}
        }
        ```
      </Tab>

      <Tab title="After">
        ```go After theme={null}
        package main

        import (
        	"context"
        	"fmt"
        	"time"

        	"github.com/langchain-ai/langsmith-go"
        )

        func main() {
        	ctx := context.Background()
        	client := langsmith.NewClient()

        	sessions, err := client.Sessions.List(ctx, langsmith.SessionListParams{
        		Name:  langsmith.F("default"),
        		Limit: langsmith.F(int64(1)),
        	})
        	if err != nil {
        		panic(err.Error())
        	}
        	projectID := sessions.Items[0].ID

        	maxStart := time.Now().UTC()
        	minStart := maxStart.AddDate(0, -1, 0)

        	iter := client.Threads.QueryAutoPaging(ctx, langsmith.ThreadQueryParams{
        		ProjectID:    langsmith.F(projectID),
        		MinStartTime: langsmith.F(minStart),
        		MaxStartTime: langsmith.F(maxStart),
        		Filter:       langsmith.F(`eq(status, "error")`),
        	})
        	for iter.Next() {
        		thread := iter.Current()
        		fmt.Println(thread.ThreadID, thread.LastError)
        	}
        	if err := iter.Err(); err != nil {
        		panic(err.Error())
        	}
        }
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="cURL">
    <Tabs sync={false}>
      <Tab title="Before">
        ```bash theme={null}
        PROJECT_ID=$(curl -s "https://api.smith.langchain.com/api/v1/sessions?name=default&limit=1" \
          -H "x-api-key: $LANGSMITH_API_KEY" | jq -r '.[0].id')

        curl -s -X POST "https://api.smith.langchain.com/api/v1/runs/query" \
          -H "x-api-key: $LANGSMITH_API_KEY" \
          -H "Content-Type: application/json" \
          -d "$(jq -n --arg pid "$PROJECT_ID" '{"session": [$pid], "is_root": true, "filter": "eq(status, \"error\")"}')" \
          | jq -r '[(.runs // [])[].extra.metadata.thread_id] | unique | .[]'
        ```
      </Tab>

      <Tab title="After">
        ```bash theme={null}
        PROJECT_ID=$(curl -s "https://api.smith.langchain.com/api/v1/sessions?name=default&limit=1" \
          -H "x-api-key: $LANGSMITH_API_KEY" | jq -r '.[0].id')

        MAX_START=$(date -u +%Y-%m-%dT%H:%M:%SZ)
        MIN_START=$(date -u -d '-1 month' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-1m +%Y-%m-%dT%H:%M:%SZ)
        curl -X POST "https://api.smith.langchain.com/api/v2/threads/query" \
          -H "x-api-key: $LANGSMITH_API_KEY" \
          -H "Content-Type: application/json" \
          -d "$(jq -n --arg pid "$PROJECT_ID" --arg min "$MIN_START" --arg max "$MAX_START" '{
            "project_id": $pid,
            "min_start_time": $min,
            "max_start_time": $max,
            "filter": "eq(status, \"error\")"
          }')"
        ```
      </Tab>
    </Tabs>
  </Tab>
</Tabs>

## Threads: list traces

Retrieve all traces belonging to a specific thread within a project.

### Main changes

#### Method name

<Tabs>
  <Tab title="Python">
    | Before | After |
    | - | - |
    | `client.read_thread()` | `client.threads.list_traces()` |

    <Note>
      `client.threads.list_traces()` is now async. Call it with `await`.
    </Note>

    See the [reference](https://reference.langchain.com/python/langsmith/_openapi_client/resources/threads/ThreadsResource/list_traces) for the full parameter and field list.
  </Tab>

  <Tab title="TypeScript">
    | Before | After |
    | - | - |
    | `client.readThread()` | `client.threads.listTraces()` |

    See the [reference](https://reference.langchain.com/javascript/langsmith/_openapi_client/Langsmith/Threads/listTraces) for the full parameter and field list.
  </Tab>

  <Tab title="Java">
    <Note>Java never had a dedicated per-thread method. The closest legacy equivalent is the generic run query filtered by the `thread_id` metadata convention.</Note>

    | Before | After |
    | - | - |
    | `client.runs().query()` (filtered by `thread_id`) | `client.threads().listTraces()` |

    See the [reference](https://javadoc.io/doc/com.langchain.smith/langsmith-java/latest/com/langchain/smith/services/blocking/ThreadService.html) for the full parameter list.
  </Tab>

  <Tab title="Go">
    <Note>Go never had a dedicated per-thread method. The closest legacy equivalent is the generic run query filtered by the `thread_id` metadata convention.</Note>

    | Before | After |
    | - | - |
    | `client.Runs.Query()` (filtered by `thread_id`) | `client.Threads.ListTraces()` |

    See the [reference](https://pkg.go.dev/github.com/langchain-ai/langsmith-go#ThreadService.ListTracesAutoPaging) for the full parameter list.
  </Tab>

  <Tab title="cURL">
    | Before | After |
    | - | - |
    | `POST /api/v1/runs/query` (`filter=eq(thread_id, ...)`) | `GET /api/v2/threads/{thread_id}/traces` |

    See the [API doc](/langsmith/smith-api/threads/query-thread-traces) for the full parameter and field list.
  </Tab>
</Tabs>

#### Query parameters

<Tabs>
  <Tab title="Python">
    `read_thread`'s `is_root` has no new equivalent. `list_traces` always returns traces (root runs) only, matching its name. `read_thread`'s `order` (asc/desc) also has no new equivalent: results are always sorted by `start_time` ascending, a fixed server-side order.

    | Before (`read_thread`) | After (`list_traces`) | Notes |
    | - | - | - |
    | `thread_id` | `thread_id` (path param) | Unchanged |
    | `project_id` XOR `project_name` | `project_id` | The new method takes only the UUID |
    | `is_root` | *(not available)* | The new method always returns traces (root runs) only |
    | `order` | *(not available)* | No sort/order field on the new method |
    | `filter` | `filter` | Same syntax, now evaluated against each root trace run |
    | `select` (arbitrary run field list) | `selects` | The new method uses `ThreadTraceSelectField`, a 24-value uppercase enum |
    | *(not available)* | `page_size` + `cursor` | The new method adds cursor pagination |
  </Tab>

  <Tab title="TypeScript">
    `readThread`'s `isRoot` has no new equivalent. `listTraces` always returns traces (root runs) only, matching its name. `readThread`'s `order` (asc/desc) also has no new equivalent: results are always sorted by `start_time` ascending, a fixed server-side order.

    | Before (`readThread`) | After (`listTraces`) | Notes |
    | - | - | - |
    | `threadId` | `threadId` (path param) | Unchanged |
    | `projectId` XOR `projectName` | `project_id` | The new method takes only the UUID |
    | `isRoot` | *(not available)* | The new method always returns traces (root runs) only |
    | `order` | *(not available)* | No sort/order field on the new method |
    | `filter` | `filter` | Same syntax, now evaluated against each root trace run |
    | `select` (arbitrary run field list) | `selects` | The new method uses a 24-value uppercase enum |
    | *(not available)* | `page_size` + `cursor` | The new method adds cursor pagination |
  </Tab>

  <Tab title="Java">
    No query parameters to map. There was no dedicated method. `listTraces(threadId, params)` takes `projectId`, `filter`, `pageSize`, `cursor`, `selects` (24-value enum). Results are always sorted by `startTime` ascending, a fixed server-side order.
  </Tab>

  <Tab title="Go">
    No query parameters to map. There was no dedicated method. `ListTraces(ctx, threadID, params)` takes `ProjectID`, `Filter`, `PageSize`, `Cursor`, `Selects` (24-value enum). Results are always sorted by `StartTime` ascending, a fixed server-side order.
  </Tab>

  <Tab title="cURL">
    `GET /api/v2/threads/{thread_id}/traces` query params: `project_id`, `filter`, `page_size`, `cursor`, `selects` (repeatable), all `snake_case`. Results are always sorted by `start_time` ascending, a fixed server-side order.
  </Tab>
</Tabs>

#### Response fields

<Tabs>
  <Tab title="Python">
    The legacy `read_thread` returns full `Run` objects (a generator). The new `ThreadTrace` is lightweight: preview fields (`inputs_preview`/`outputs_preview`) instead of full `inputs`/`outputs`, no embedded child runs. `selects` controls what's populated, the same as `traces.query`.

    | Before (legacy `Run` field, via `read_thread`) | After (new `ThreadTrace` field) | Notes |
    | - | - | - |
    | `id` | *(not available)* | the legacy root run `id` and `trace_id` were identical; the new API exposes only `trace_id` |
    | `trace_id` | `trace_id` | Returned by default when `selects` is omitted |
    | `name` | `name` | Omitted unless included in `selects` |
    | `start_time` | `start_time` | Omitted unless included in `selects` |
    | `end_time` | `end_time` | Omitted unless included in `selects` |
    | `run_type` | `op` | Renamed; encoded as a number instead of a string |
    | `inputs` | `inputs_preview`, or `inputs` for the untruncated payload | Truncated preview by default; select `INPUTS` for the full payload |
    | `outputs` | `outputs_preview`, or `outputs` for the untruncated payload | Truncated preview by default; select `OUTPUTS` for the full payload |
    | `error` | `error_preview`, or `error` for the full message | Truncated summary by default; select `ERROR` for the full error message |
    | `latency` (property) | `latency` | Native field instead of a computed `timedelta` property |
    | `total_tokens`, `prompt_tokens`, `completion_tokens` | `total_tokens`, `prompt_tokens`, `completion_tokens` | Unchanged |
    | `total_cost`, `prompt_cost`, `completion_cost` | `total_cost`, `prompt_cost`, `completion_cost` | Unchanged |
    | `prompt_token_details`, `completion_token_details` | `prompt_token_details`, `completion_token_details` | Field now wraps the dict; access `.raw` |
    | `prompt_cost_details`, `completion_cost_details` | `prompt_cost_details`, `completion_cost_details` | Field now wraps the dict; access `.raw` |
    | `first_token_time` | `first_token_time` | Omitted unless included in `selects` |
    | *(not available)* | `thread_id` | New: the thread UUID this trace belongs to |
    | `child_runs`, `child_run_ids` | *(not available)* | No embedded child runs; use `traces.list_runs` for descendant runs |
  </Tab>

  <Tab title="TypeScript">
    The legacy `readThread` returns full `Run` objects (an async generator). The new `ThreadTrace` is lightweight: preview fields (`inputs_preview`/`outputs_preview`) instead of full `inputs`/`outputs`, no embedded child runs. `selects` controls what is populated, the same as `traces.query`.

    | Before (legacy `Run` field, via `readThread`) | After (new `ThreadTrace` field) | Notes |
    | - | - | - |
    | `id` | *(not available)* | the legacy root run `id` and `trace_id` were identical; the new API exposes only `trace_id` |
    | `trace_id` | `trace_id` | Returned by default when `selects` is omitted |
    | `name` | `name` | Omitted unless included in `selects` |
    | `start_time` | `start_time` | Omitted unless included in `selects` |
    | `end_time` | `end_time` | Omitted unless included in `selects` |
    | `run_type` | `op` | Renamed; encoded as a number instead of a string |
    | `inputs` | `inputs_preview`, or `inputs` for the untruncated payload | Truncated preview by default; select `INPUTS` for the full payload |
    | `outputs` | `outputs_preview`, or `outputs` for the untruncated payload | Truncated preview by default; select `OUTPUTS` for the full payload |
    | `error` | `error_preview`, or `error` for the full message | Truncated summary by default; select `ERROR` for the full error message |
    | `latency` | `latency` | Native field on the new type |
    | `total_tokens`, `prompt_tokens`, `completion_tokens` | `total_tokens`, `prompt_tokens`, `completion_tokens` | Unchanged |
    | `total_cost`, `prompt_cost`, `completion_cost` | `total_cost`, `prompt_cost`, `completion_cost` | Unchanged |
    | `prompt_token_details`, `completion_token_details` | `prompt_token_details`, `completion_token_details` | Unchanged |
    | `prompt_cost_details`, `completion_cost_details` | `prompt_cost_details`, `completion_cost_details` | Unchanged |
    | `first_token_time` | `first_token_time` | Omitted unless included in `selects` |
    | *(not available)* | `thread_id` | New: the thread UUID this trace belongs to |
    | `child_runs`, `child_run_ids` | *(not available)* | No embedded child runs; use `traces.listRuns` for descendant runs |
  </Tab>

  <Tab title="Java">
    `ThreadTrace` has 24 `Optional` fields: `traceId`, `threadId`, `name`, `startTime`, `endTime`, `latency`, `op`, token/cost fields with per-category `_details`, `inputsPreview`/`outputsPreview`/`inputs`/`outputs`, `errorPreview`/`error`, `firstTokenTime`.

    | Before (legacy `RunSchema` method) | After (new `ThreadTrace` method) | Notes |
    | - | - | - |
    | `id()` | *(not available)* | the legacy root run `id()` and `traceId()` were identical; the new API exposes only `traceId()` |
    | `traceId()` | `traceId()` | Returned by default when `selects` is omitted |
    | `name()` | `name()` | Omitted unless included in `selects` |
    | `startTime()` | `startTime()` | Omitted unless included in `selects` |
    | `endTime()` | `endTime()` | Omitted unless included in `selects` |
    | `runType()` | `op()` | Renamed; encoded as a number instead of a string |
    | `inputs()` | `inputsPreview()`, or `inputs()` for the untruncated payload | Truncated preview by default; select `INPUTS` for the full payload |
    | `outputs()` | `outputsPreview()`, or `outputs()` for the untruncated payload | Truncated preview by default; select `OUTPUTS` for the full payload |
    | `error()` | `errorPreview()`, or `error()` for the full message | Truncated summary by default; select `ERROR` for the full error message |
    | `latency()` | `latency()` | Unchanged |
    | `totalTokens()`, `promptTokens()`, `completionTokens()` | `totalTokens()`, `promptTokens()`, `completionTokens()` | Unchanged |
    | `totalCost()`, `promptCost()`, `completionCost()` | `totalCost()`, `promptCost()`, `completionCost()` | Unchanged |
    | `promptTokenDetails()`, `completionTokenDetails()` | `promptTokenDetails()`, `completionTokenDetails()` | Unchanged |
    | `promptCostDetails()`, `completionCostDetails()` | `promptCostDetails()`, `completionCostDetails()` | Unchanged |
    | `firstTokenTime()` | `firstTokenTime()` | Omitted unless included in `selects` |
    | *(not available)* | `threadId()` | New: the thread UUID this trace belongs to |
    | `childRuns()`, `childRunIds()` | *(not available)* | No embedded child runs; use `traces().listRuns()` for descendant runs |
  </Tab>

  <Tab title="Go">
    `ThreadTrace` has 24 fields, in `PascalCase` Go struct form.

    | Before (legacy root `Run` field) | After (new `ThreadTrace` field) | Notes |
    | - | - | - |
    | `ID` | *(not available)* | the legacy root run `ID` and `TraceID` were identical; the new API exposes only `TraceID` |
    | `TraceID` | `TraceID` | Returned by default when `Selects` is omitted |
    | `Name` | `Name` | Omitted unless included in `Selects` |
    | `StartTime` | `StartTime` | Omitted unless included in `Selects` |
    | `EndTime` | `EndTime` | Omitted unless included in `Selects` |
    | `RunType` | `Op` | Renamed; encoded as a number instead of a string |
    | `Inputs` | `InputsPreview`, or `Inputs` for the untruncated payload | Truncated preview by default; select `INPUTS` for the full payload |
    | `Outputs` | `OutputsPreview`, or `Outputs` for the untruncated payload | Truncated preview by default; select `OUTPUTS` for the full payload |
    | `Error` | `ErrorPreview`, or `Error` for the full message | Truncated summary by default; select `ERROR` for the full error message |
    | `Latency` | `Latency` | Unchanged |
    | `TotalTokens`, `PromptTokens`, `CompletionTokens` | `TotalTokens`, `PromptTokens`, `CompletionTokens` | Unchanged |
    | `TotalCost`, `PromptCost`, `CompletionCost` | `TotalCost`, `PromptCost`, `CompletionCost` | Unchanged |
    | `PromptTokenDetails`, `CompletionTokenDetails` | `PromptTokenDetails`, `CompletionTokenDetails` | Unchanged |
    | `PromptCostDetails`, `CompletionCostDetails` | `PromptCostDetails`, `CompletionCostDetails` | Unchanged |
    | `FirstTokenTime` | `FirstTokenTime` | Omitted unless included in `Selects` |
    | *(not available)* | `ThreadID` | New: the thread UUID this trace belongs to |
    | `ChildRuns`, `ChildRunIDs` | *(not available)* | No embedded child runs; use `Traces.ListRuns` for descendant runs |
  </Tab>

  <Tab title="cURL">
    JSON response fields use `snake_case`, matching the table below.

    | Before (legacy root run field) | After (new `ThreadTrace` field) | Notes |
    | - | - | - |
    | `id` | *(not available)* | the legacy root run `id` and `trace_id` were identical; the new API exposes only `trace_id` |
    | `trace_id` | `trace_id` | Returned by default when `selects` is omitted |
    | `name` | `name` | Omitted unless included in `selects` |
    | `start_time` | `start_time` | Omitted unless included in `selects` |
    | `end_time` | `end_time` | Omitted unless included in `selects` |
    | `run_type` | `op` | Renamed; encoded as a number instead of a string |
    | `inputs` | `inputs_preview`, or `inputs` for the untruncated payload | Truncated preview by default; select `INPUTS` for the full payload |
    | `outputs` | `outputs_preview`, or `outputs` for the untruncated payload | Truncated preview by default; select `OUTPUTS` for the full payload |
    | `error` | `error_preview`, or `error` for the full message | Truncated summary by default; select `ERROR` for the full error message |
    | `latency` | `latency` | Unchanged |
    | `total_tokens`, `prompt_tokens`, `completion_tokens` | `total_tokens`, `prompt_tokens`, `completion_tokens` | Unchanged |
    | `total_cost`, `prompt_cost`, `completion_cost` | `total_cost`, `prompt_cost`, `completion_cost` | Unchanged |
    | `prompt_token_details`, `completion_token_details` | `prompt_token_details`, `completion_token_details` | Unchanged |
    | `prompt_cost_details`, `completion_cost_details` | `prompt_cost_details`, `completion_cost_details` | Unchanged |
    | `first_token_time` | `first_token_time` | Omitted unless included in `selects` |
    | *(not available)* | `thread_id` | New: the thread UUID this trace belongs to |
    | `child_runs`, `child_run_ids` | *(not available)* | No embedded child runs; use `traces.list_runs` for descendant runs |
  </Tab>
</Tabs>

### Examples

#### List every trace (turn) in a thread

Fetch all the traces (conversation turns) that belong to one thread.

<Tabs>
  <Tab title="Python">
    <Tabs sync={false}>
      <Tab title="Before">
        ```python Before theme={null}
        from langsmith import Client

        client = Client()
        thread_id = "<thread-id>"
        for run in client.read_thread(thread_id=thread_id, project_name="default"):
            print(run.id, run.start_time)
        ```
      </Tab>

      <Tab title="After">
        ```python After theme={null}
        import asyncio

        from langsmith import Client


        async def main():
            client = Client()
            project = await client.aread_project(project_name="default")
            thread_id = "<thread-id>"
            async for trace in client.threads.list_traces(
                thread_id, project_id=str(project.id), selects=["TRACE_ID", "START_TIME"]
            ):
                print(trace.trace_id, trace.start_time)


        asyncio.run(main())
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="TypeScript">
    <Tabs sync={false}>
      <Tab title="Before">
        ```ts Before theme={null}
        import { Client } from "langsmith";

        const client = new Client();
        let threadId = "<thread-id>";
        for await (const run of client.readThread({ threadId, projectName: "default" })) {
          console.log(run.id, run.start_time);
        }
        ```
      </Tab>

      <Tab title="After">
        ```ts After theme={null}
        import { Client } from "langsmith";

        const client = new Client();
        const project = await client.readProject({ projectName: "default" });
        let threadId = "<thread-id>";
        for await (const trace of client.threads.listTraces(threadId, {
          project_id: project.id,
          selects: ["TRACE_ID", "START_TIME"],
        })) {
          console.log(trace.trace_id, trace.start_time);
        }
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Java">
    <Tabs sync={false}>
      <Tab title="Before">
        ```kotlin Before theme={null}

        import com.langchain.smith.client.LangsmithClient
        import com.langchain.smith.client.okhttp.LangsmithOkHttpClient
        import com.langchain.smith.models.runs.RunQueryParams
        import com.langchain.smith.models.sessions.SessionListParams

        val client: LangsmithClient = LangsmithOkHttpClient.fromEnv()

        val project = client.sessions().list(
            SessionListParams.builder().name("default").limit(1L).build()
        ).items().first()

        var threadId = "<thread-id>"

        val runs = client.runs().query(
            RunQueryParams.builder()
                .addSession(project.id())
                .isRoot(true)
                .filter("eq(thread_id, \"$threadId\")")
                .build()
        ).runs()
        for (run in runs) {
            println("${run.id()} ${run.startTime().get()}")
        }
        ```
      </Tab>

      <Tab title="After">
        ```kotlin After theme={null}

        import com.langchain.smith.client.LangsmithClient
        import com.langchain.smith.client.okhttp.LangsmithOkHttpClient
        import com.langchain.smith.models.sessions.SessionListParams
        import com.langchain.smith.models.threads.ThreadListTracesParams

        val client: LangsmithClient = LangsmithOkHttpClient.fromEnv()

        val project = client.sessions().list(
            SessionListParams.builder().name("default").limit(1L).build()
        ).items().first()

        var threadId = "<thread-id>"

        val traces = client.threads().listTraces(
            threadId,
            ThreadListTracesParams.builder()
                .projectId(project.id())
                .addSelect(ThreadListTracesParams.Select.TRACE_ID)
                .addSelect(ThreadListTracesParams.Select.START_TIME)
                .build()
        ).items()
        for (trace in traces) {
            println("${trace.traceId().get()} ${trace.startTime().get()}")
        }
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Go">
    <Tabs sync={false}>
      <Tab title="Before">
        ```go Before theme={null}
        package main

        import (
        	"context"
        	"fmt"
        	"time"

        	"github.com/langchain-ai/langsmith-go"
        )

        func main() {
        	ctx := context.Background()
        	client := langsmith.NewClient()

        	sessions, err := client.Sessions.List(ctx, langsmith.SessionListParams{
        		Name:  langsmith.F("default"),
        		Limit: langsmith.F(int64(1)),
        	})
        	if err != nil {
        		panic(err.Error())
        	}
        	projectID := sessions.Items[0].ID
        	threadID := "<thread-id>"

        	runs, err := client.Runs.Query(ctx, langsmith.RunQueryParams{
        		Session: langsmith.F([]string{projectID}),
        		IsRoot:  langsmith.F(true),
        		Filter:  langsmith.F(fmt.Sprintf(`eq(thread_id, "%s")`, threadID)),
        	})
        	if err != nil {
        		panic(err.Error())
        	}
        	for _, run := range runs.Runs {
        		fmt.Println(run.ID, run.StartTime)
        	}
        }
        ```
      </Tab>

      <Tab title="After">
        ```go After theme={null}
        package main

        import (
        	"context"
        	"fmt"
        	"time"

        	"github.com/langchain-ai/langsmith-go"
        )

        func main() {
        	ctx := context.Background()
        	client := langsmith.NewClient()

        	sessions, err := client.Sessions.List(ctx, langsmith.SessionListParams{
        		Name:  langsmith.F("default"),
        		Limit: langsmith.F(int64(1)),
        	})
        	if err != nil {
        		panic(err.Error())
        	}
        	projectID := sessions.Items[0].ID
        	threadID := "<thread-id>"

        	iter := client.Threads.ListTracesAutoPaging(ctx, threadID, langsmith.ThreadListTracesParams{
        		ProjectID: langsmith.F(projectID),
        		Selects:   langsmith.F([]langsmith.ThreadListTracesParamsSelect{langsmith.ThreadListTracesParamsSelectTraceID, langsmith.ThreadListTracesParamsSelectStartTime}),
        	})
        	for iter.Next() {
        		trace := iter.Current()
        		fmt.Println(trace.TraceID, trace.StartTime)
        	}
        	if err := iter.Err(); err != nil {
        		panic(err.Error())
        	}
        }
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="cURL">
    <Tabs sync={false}>
      <Tab title="Before">
        ```bash theme={null}
        PROJECT_ID=$(curl -s "https://api.smith.langchain.com/api/v1/sessions?name=default&limit=1" \
          -H "x-api-key: $LANGSMITH_API_KEY" | jq -r '.[0].id')
        THREAD_ID="<thread-id>"

        curl -s -X POST "https://api.smith.langchain.com/api/v1/runs/query" \
          -H "x-api-key: $LANGSMITH_API_KEY" \
          -H "Content-Type: application/json" \
          -d "$(jq -n --arg pid "$PROJECT_ID" --arg tid "$THREAD_ID" '{"session": [$pid], "is_root": true, "filter": ("eq(thread_id, \"" + $tid + "\")")}')" \
          | jq '.runs // []'
        ```
      </Tab>

      <Tab title="After">
        ```bash theme={null}
        PROJECT_ID=$(curl -s "https://api.smith.langchain.com/api/v1/sessions?name=default&limit=1" \
          -H "x-api-key: $LANGSMITH_API_KEY" | jq -r '.[0].id')
        THREAD_ID="<thread-id>"

        curl -G "https://api.smith.langchain.com/api/v2/threads/$THREAD_ID/traces" \
          -H "x-api-key: $LANGSMITH_API_KEY" \
          --data-urlencode "project_id=$PROJECT_ID" \
          --data-urlencode "selects=TRACE_ID" \
          --data-urlencode "selects=START_TIME"
        ```
      </Tab>
    </Tabs>
  </Tab>
</Tabs>

#### Select specific trace's fields

Request just the fields you need instead of every field, to reduce response size.

<Tabs>
  <Tab title="Python">
    <Tabs sync={false}>
      <Tab title="Before">
        ```python Before theme={null}
        from langsmith import Client

        client = Client()
        thread_id = "<thread-id>"
        for run in client.read_thread(
            thread_id=thread_id,
            project_name="default",
            select=["id", "total_tokens", "total_cost"],
        ):
            print(run.id, run.total_tokens, run.total_cost)
        ```
      </Tab>

      <Tab title="After">
        ```python After theme={null}
        import asyncio

        from langsmith import Client


        async def main():
            client = Client()
            project = await client.aread_project(project_name="default")
            thread_id = "<thread-id>"
            async for trace in client.threads.list_traces(
                thread_id,
                project_id=str(project.id),
                selects=["TRACE_ID", "TOTAL_TOKENS", "TOTAL_COST"],
            ):
                print(trace.trace_id, trace.total_tokens, trace.total_cost)


        asyncio.run(main())
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="TypeScript">
    <Tabs sync={false}>
      <Tab title="Before">
        ```ts Before theme={null}
        import { Client } from "langsmith";

        const client = new Client();
        let threadId = "<thread-id>";
        for await (const run of client.readThread({
          threadId,
          projectName: "default",
          select: ["id", "total_tokens", "total_cost"],
        })) {
          console.log(run.id, run.total_tokens, run.total_cost);
        }
        ```
      </Tab>

      <Tab title="After">
        ```ts After theme={null}
        import { Client } from "langsmith";

        const client = new Client();
        const project = await client.readProject({ projectName: "default" });
        let threadId = "<thread-id>";
        for await (const trace of client.threads.listTraces(threadId, {
          project_id: project.id,
          selects: ["TRACE_ID", "TOTAL_TOKENS", "TOTAL_COST"],
        })) {
          console.log(trace.trace_id, trace.total_tokens, trace.total_cost);
        }
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Java">
    <Note>The Before example omits `total_cost` here. Selecting it on the legacy `RunSchema` type triggers a known deserialization bug in the current Java binding (it expects a string, the API returns a number).</Note>

    <Tabs sync={false}>
      <Tab title="Before">
        ```kotlin Before theme={null}

        import com.langchain.smith.client.LangsmithClient
        import com.langchain.smith.client.okhttp.LangsmithOkHttpClient
        import com.langchain.smith.models.runs.RunQueryParams
        import com.langchain.smith.models.sessions.SessionListParams
        import kotlin.jvm.optionals.getOrNull

        val client: LangsmithClient = LangsmithOkHttpClient.fromEnv()

        val project = client.sessions().list(
            SessionListParams.builder().name("default").limit(1L).build()
        ).items().first()

        var threadId = "<thread-id>"

        // Note: selecting total_cost here triggers a known deserialization bug in the
        // v1 Java binding (RunSchema.totalCost() expects a string, the API returns a
        // number) — omitted to keep this example runnable; see the migration notes.
        val runs = client.runs().query(
            RunQueryParams.builder()
                .addSession(project.id())
                .isRoot(true)
                .filter("eq(thread_id, \"$threadId\")")
                .addSelect(RunQueryParams.Select.ID)
                .addSelect(RunQueryParams.Select.TOTAL_TOKENS)
                .build()
        ).runs()
        for (run in runs) {
            println("${run.id()} ${run.totalTokens().getOrNull()}")
        }
        ```
      </Tab>

      <Tab title="After">
        ```kotlin After theme={null}

        import com.langchain.smith.client.LangsmithClient
        import com.langchain.smith.client.okhttp.LangsmithOkHttpClient
        import com.langchain.smith.models.sessions.SessionListParams
        import com.langchain.smith.models.threads.ThreadListTracesParams
        import kotlin.jvm.optionals.getOrNull

        val client: LangsmithClient = LangsmithOkHttpClient.fromEnv()

        val project = client.sessions().list(
            SessionListParams.builder().name("default").limit(1L).build()
        ).items().first()

        var threadId = "<thread-id>"

        val traces = client.threads().listTraces(
            threadId,
            ThreadListTracesParams.builder()
                .projectId(project.id())
                .addSelect(ThreadListTracesParams.Select.TRACE_ID)
                .addSelect(ThreadListTracesParams.Select.TOTAL_TOKENS)
                .addSelect(ThreadListTracesParams.Select.TOTAL_COST)
                .build()
        ).items()
        for (trace in traces) {
            println("${trace.traceId().get()} ${trace.totalTokens().getOrNull()} ${trace.totalCost().getOrNull()}")
        }
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Go">
    <Tabs sync={false}>
      <Tab title="Before">
        ```go Before theme={null}
        package main

        import (
        	"context"
        	"fmt"
        	"time"

        	"github.com/langchain-ai/langsmith-go"
        )

        func main() {
        	ctx := context.Background()
        	client := langsmith.NewClient()

        	sessions, err := client.Sessions.List(ctx, langsmith.SessionListParams{
        		Name:  langsmith.F("default"),
        		Limit: langsmith.F(int64(1)),
        	})
        	if err != nil {
        		panic(err.Error())
        	}
        	projectID := sessions.Items[0].ID
        	threadID := "<thread-id>"

        	runs, err := client.Runs.Query(ctx, langsmith.RunQueryParams{
        		Session: langsmith.F([]string{projectID}),
        		IsRoot:  langsmith.F(true),
        		Filter:  langsmith.F(fmt.Sprintf(`eq(thread_id, "%s")`, threadID)),
        		Select: langsmith.F([]langsmith.RunQueryParamsSelect{
        			langsmith.RunQueryParamsSelectID,
        			langsmith.RunQueryParamsSelectTotalTokens,
        			langsmith.RunQueryParamsSelectTotalCost,
        		}),
        	})
        	if err != nil {
        		panic(err.Error())
        	}
        	for _, run := range runs.Runs {
        		fmt.Println(run.ID, run.TotalTokens, run.TotalCost)
        	}
        }
        ```
      </Tab>

      <Tab title="After">
        ```go After theme={null}
        package main

        import (
        	"context"
        	"fmt"
        	"time"

        	"github.com/langchain-ai/langsmith-go"
        )

        func main() {
        	ctx := context.Background()
        	client := langsmith.NewClient()

        	sessions, err := client.Sessions.List(ctx, langsmith.SessionListParams{
        		Name:  langsmith.F("default"),
        		Limit: langsmith.F(int64(1)),
        	})
        	if err != nil {
        		panic(err.Error())
        	}
        	projectID := sessions.Items[0].ID
        	threadID := "<thread-id>"

        	iter := client.Threads.ListTracesAutoPaging(ctx, threadID, langsmith.ThreadListTracesParams{
        		ProjectID: langsmith.F(projectID),
        		Selects: langsmith.F([]langsmith.ThreadListTracesParamsSelect{
        			langsmith.ThreadListTracesParamsSelectTraceID,
        			langsmith.ThreadListTracesParamsSelectTotalTokens,
        			langsmith.ThreadListTracesParamsSelectTotalCost,
        		}),
        	})
        	for iter.Next() {
        		trace := iter.Current()
        		fmt.Println(trace.TraceID, trace.TotalTokens, trace.TotalCost)
        	}
        	if err := iter.Err(); err != nil {
        		panic(err.Error())
        	}
        }
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="cURL">
    <Tabs sync={false}>
      <Tab title="Before">
        ```bash theme={null}
        PROJECT_ID=$(curl -s "https://api.smith.langchain.com/api/v1/sessions?name=default&limit=1" \
          -H "x-api-key: $LANGSMITH_API_KEY" | jq -r '.[0].id')
        THREAD_ID="<thread-id>"

        curl -s -X POST "https://api.smith.langchain.com/api/v1/runs/query" \
          -H "x-api-key: $LANGSMITH_API_KEY" \
          -H "Content-Type: application/json" \
          -d "$(jq -n --arg pid "$PROJECT_ID" --arg tid "$THREAD_ID" '{"session": [$pid], "is_root": true, "filter": ("eq(thread_id, \"" + $tid + "\")"), "select": ["id", "total_tokens", "total_cost"]}')" \
          | jq '.runs // []'
        ```
      </Tab>

      <Tab title="After">
        ```bash theme={null}
        PROJECT_ID=$(curl -s "https://api.smith.langchain.com/api/v1/sessions?name=default&limit=1" \
          -H "x-api-key: $LANGSMITH_API_KEY" | jq -r '.[0].id')
        THREAD_ID="<thread-id>"

        curl -G "https://api.smith.langchain.com/api/v2/threads/$THREAD_ID/traces" \
          -H "x-api-key: $LANGSMITH_API_KEY" \
          --data-urlencode "project_id=$PROJECT_ID" \
          --data-urlencode "selects=TRACE_ID" \
          --data-urlencode "selects=TOTAL_TOKENS" \
          --data-urlencode "selects=TOTAL_COST"
        ```
      </Tab>
    </Tabs>
  </Tab>
</Tabs>

## See also

* [Traces](/langsmith/smithdb-sdk-migration-traces)
* [Dataset experiment runs](/langsmith/smithdb-sdk-migration-experiments)
* [Migration overview](/langsmith/smithdb-sdk-migration)

***

<div className="source-links">
  <Callout icon="terminal-2">
    [Connect these docs](/use-these-docs) to your agent of choice via MCP for real-time answers.
  </Callout>

  <Callout icon="edit">
    [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/smithdb-sdk-migration-threads.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
  </Callout>
</div>
