## Research `client.Agent.Research(ctx, body) (*ResearchEventUnion, error)` **post** `/research` Execute AI-powered research queries that search the web, analyze sources, and synthesize comprehensive answers. This endpoint **always streams** responses using Server-Sent Events (SSE). **Streaming Response:** - All responses are streamed using Server-Sent Events (`text/event-stream`) - Real-time progress updates as research progresses through phases **Research Modes:** - `fast` - Quick answers with minimal web searches (default) - `balanced` - Standard research with multiple iterations **Use Cases:** - Answering complex questions with cited sources - Synthesizing information from multiple web sources - Research reports on specific topics - Fact-checking and verification tasks ### Parameters - `body AgentResearchParams` - `Query param.Field[string]` The research query or question to answer. Maximum 10,000 characters. - `FetchTimeout param.Field[int64]` Timeout in seconds for fetching web pages - `Mode param.Field[AgentResearchParamsMode]` Research mode: fast (quick answers, default), balanced (standard research) - `const AgentResearchParamsModeFast AgentResearchParamsMode = "fast"` - `const AgentResearchParamsModeBalanced AgentResearchParamsMode = "balanced"` - `Nocache param.Field[bool]` Skip cache and force fresh research ### Returns - `type ResearchEventUnion interface{…}` A Server-Sent Event from /v1/research. Typed discriminated union keyed on event. - `type ResearchEventAnalyzingEnd struct{…}` Envelope for the "analyzing:end" event from /v1/research. - `Data ResearchEventAnalyzingEndData` - `Analyzed float64` - `Failed float64` - `Iteration float64` - `Message string` - `Samples []ResearchEventAnalyzingEndDataSample` - `Domain string` - `Title string` - `URL string` - `URLSource string` URL source tracking - where a URL came from - `const ResearchEventAnalyzingEndDataSampleURLSourceUserInput ResearchEventAnalyzingEndDataSampleURLSource = "user-input"` - `const ResearchEventAnalyzingEndDataSampleURLSourceSearchResult ResearchEventAnalyzingEndDataSampleURLSource = "search-result"` - `const ResearchEventAnalyzingEndDataSampleURLSourceExtractedLink ResearchEventAnalyzingEndDataSampleURLSource = "extracted-link"` - `Relevance string` - `const ResearchEventAnalyzingEndDataSampleRelevanceLow ResearchEventAnalyzingEndDataSampleRelevance = "low"` - `const ResearchEventAnalyzingEndDataSampleRelevanceMedium ResearchEventAnalyzingEndDataSampleRelevance = "medium"` - `const ResearchEventAnalyzingEndDataSampleRelevanceHigh ResearchEventAnalyzingEndDataSampleRelevance = "high"` - `Reliability string` - `const ResearchEventAnalyzingEndDataSampleReliabilityLow ResearchEventAnalyzingEndDataSampleReliability = "low"` - `const ResearchEventAnalyzingEndDataSampleReliabilityMedium ResearchEventAnalyzingEndDataSampleReliability = "medium"` - `const ResearchEventAnalyzingEndDataSampleReliabilityHigh ResearchEventAnalyzingEndDataSampleReliability = "high"` - `Summary string` - `Timestamp float64` - `Event AnalyzingEnd` - `const AnalyzingEndAnalyzingEnd AnalyzingEnd = "analyzing:end"` - `type ResearchEventAnalyzingStart struct{…}` Envelope for the "analyzing:start" event from /v1/research. - `Data ResearchEventAnalyzingStartData` - `Iteration float64` - `Message string` - `PageCount float64` - `Timestamp float64` - `Event AnalyzingStart` - `const AnalyzingStartAnalyzingStart AnalyzingStart = "analyzing:start"` - `type ResearchEventComplete struct{…}` Envelope for the "complete" event from /v1/research. - `Data ResearchEventCompleteData` complete - Research finished successfully - `Message string` - `Metadata ResearchEventCompleteDataMetadata` Research metadata Note: citedPages, gapEvaluations, outline, and judgments are optional to support fast mode, which skips these phases for maximum speed. - `ExecutedQueries [][]string` - `Mode string` Research mode determines depth, thinking budget, and quality controls Modes (in order of cost/thoroughness): - **fast**: Quick answers with minimal validation (~$2, 1 iteration, no judge) - **balanced**: Standard research with moderate depth (~$8, 3 iterations, Flash models, no judge) - **deep**: Thorough research with judge review (~$15, 5 iterations, Flash models, with judge) - **max**: Maximum quality with Pro models (~$40, 5 iterations, Pro models, with judge) - **ultra**: Ultimate tier - all Pro models, 10 iterations (expensive, for when accuracy is paramount) - `const ResearchEventCompleteDataMetadataModeFast ResearchEventCompleteDataMetadataMode = "fast"` - `const ResearchEventCompleteDataMetadataModeBalanced ResearchEventCompleteDataMetadataMode = "balanced"` - `const ResearchEventCompleteDataMetadataModeDeep ResearchEventCompleteDataMetadataMode = "deep"` - `const ResearchEventCompleteDataMetadataModeMax ResearchEventCompleteDataMetadataMode = "max"` - `const ResearchEventCompleteDataMetadataModeUltra ResearchEventCompleteDataMetadataMode = "ultra"` - `Prompt string` - `QueryComplexity string` - `const ResearchEventCompleteDataMetadataQueryComplexitySimple ResearchEventCompleteDataMetadataQueryComplexity = "simple"` - `const ResearchEventCompleteDataMetadataQueryComplexityModerate ResearchEventCompleteDataMetadataQueryComplexity = "moderate"` - `const ResearchEventCompleteDataMetadataQueryComplexityComplex ResearchEventCompleteDataMetadataQueryComplexity = "complex"` - `ResearchObjective string` - `ResearchPlan string` - `ResearchQuestions []string` - `TotalPagesAnalyzed float64` Total pages analyzed across all iterations - `CitedPages []ResearchEventCompleteDataMetadataCitedPage` Pages cited in the report, ordered by first citation appearance - `ID string` - `Claims []string` - `SourceQueries []string` - `URL string` - `Depth float64` - `FullText string` Full page text (fetched markdown or search excerpts). Only populated when `includeFullText: true` in ResearchOptions. - Fast mode: Parallel API excerpts (~5000 chars) - Other modes: Fetched page markdown - `ParentURL string` - `Relevance string` - `const ResearchEventCompleteDataMetadataCitedPageRelevanceLow ResearchEventCompleteDataMetadataCitedPageRelevance = "low"` - `const ResearchEventCompleteDataMetadataCitedPageRelevanceMedium ResearchEventCompleteDataMetadataCitedPageRelevance = "medium"` - `const ResearchEventCompleteDataMetadataCitedPageRelevanceHigh ResearchEventCompleteDataMetadataCitedPageRelevance = "high"` - `Reliability string` - `const ResearchEventCompleteDataMetadataCitedPageReliabilityLow ResearchEventCompleteDataMetadataCitedPageReliability = "low"` - `const ResearchEventCompleteDataMetadataCitedPageReliabilityMedium ResearchEventCompleteDataMetadataCitedPageReliability = "medium"` - `const ResearchEventCompleteDataMetadataCitedPageReliabilityHigh ResearchEventCompleteDataMetadataCitedPageReliability = "high"` - `Summary string` LLM-generated summary. Undefined in fast mode (no content analysis). - `Title string` - `URLSource string` URL source tracking - where a URL came from - `const ResearchEventCompleteDataMetadataCitedPageURLSourceUserInput ResearchEventCompleteDataMetadataCitedPageURLSource = "user-input"` - `const ResearchEventCompleteDataMetadataCitedPageURLSourceSearchResult ResearchEventCompleteDataMetadataCitedPageURLSource = "search-result"` - `const ResearchEventCompleteDataMetadataCitedPageURLSourceExtractedLink ResearchEventCompleteDataMetadataCitedPageURLSource = "extracted-link"` - `GapEvaluations []ResearchEventCompleteDataMetadataGapEvaluation` - `GapDescription string` Based on unanswered/partial questions, what specific information is still needed? - `QuestionAssessments []ResearchEventCompleteDataMetadataGapEvaluationQuestionAssessment` Assessment of each research question's status and findings - `Findings string` What we learned (if answered/partial) or what's missing (if unanswered) - `Question string` The research question being assessed - `Status string` Status: answered (clear info), partial (some info, gaps remain), unanswered (no relevant info) - `const ResearchEventCompleteDataMetadataGapEvaluationQuestionAssessmentStatusAnswered ResearchEventCompleteDataMetadataGapEvaluationQuestionAssessmentStatus = "answered"` - `const ResearchEventCompleteDataMetadataGapEvaluationQuestionAssessmentStatusPartial ResearchEventCompleteDataMetadataGapEvaluationQuestionAssessmentStatus = "partial"` - `const ResearchEventCompleteDataMetadataGapEvaluationQuestionAssessmentStatusUnanswered ResearchEventCompleteDataMetadataGapEvaluationQuestionAssessmentStatus = "unanswered"` - `ResearchCoverage string` Research coverage level - assesses quality across all questions. Hierarchy: Light < Moderate < Solid < Comprehensive - **Light**: Basic info on some questions, most need more depth → Continue - **Moderate**: Multiple questions answered, some remain partial → Continue - **Solid**: Most questions well-answered with validated sources → Sufficient to stop - **Comprehensive**: All questions thoroughly answered, exceptional depth → Definitely stop - `const ResearchEventCompleteDataMetadataGapEvaluationResearchCoverageLight ResearchEventCompleteDataMetadataGapEvaluationResearchCoverage = "Light"` - `const ResearchEventCompleteDataMetadataGapEvaluationResearchCoverageModerate ResearchEventCompleteDataMetadataGapEvaluationResearchCoverage = "Moderate"` - `const ResearchEventCompleteDataMetadataGapEvaluationResearchCoverageSolid ResearchEventCompleteDataMetadataGapEvaluationResearchCoverage = "Solid"` - `const ResearchEventCompleteDataMetadataGapEvaluationResearchCoverageComprehensive ResearchEventCompleteDataMetadataGapEvaluationResearchCoverage = "Comprehensive"` - `ShouldContinueResearch bool` Explicit decision: should research continue with another iteration? - Considers: how many questions unanswered/partial, coverage for mode, remaining iterations - Drives query generation: true → generate queries, false → stop researching - `NewResearchQuestions []string` New research questions to add (optional, use sparingly) - Only if original decomposition missed something critical - Maximum 2-3 new questions total across all iterations - Most iterations should return empty array or omit this field - `SearchQueries []string` Search queries to address identified gaps (only when shouldContinueResearch is true) - Target unanswered questions first, then partial questions - 3-10 targeted queries if shouldContinueResearch is true - Omit or provide empty array if shouldContinueResearch is false - `Judgments []ResearchEventCompleteDataMetadataJudgment` - `Approved bool` - `Observation string` - `Score float64` - `Feedback string` - `Metrics ResearchEventCompleteDataMetadataMetrics` Complete research metrics - `CachedFetches float64` Cached fetch count (subset of fetches) - `CachedSearches map[string, float64]` Cached search count by provider name (subset of searches) - `Fetches float64` Fetch count (number of pages fetched) - `Iterations float64` Number of research iterations performed - `Phases map[string, ResearchEventCompleteDataMetadataMetricsPhase]` Phase timings with duration in milliseconds - `Duration float64` - `RobotsBlocked float64` Number of URLs blocked by robots.txt - `Searches map[string, float64]` Search count by provider name (e.g., "bright-data", "parallel") - `SuccessRates ResearchEventCompleteDataMetadataMetricsSuccessRates` Success rate metrics - `Analyzes float64` - `Fetches float64` - `Searches float64` - `Tokens map[string, ResearchEventCompleteDataMetadataMetricsToken]` Token usage by model ID (e.g., "gemini-2.5-flash") - `Input float64` - `Output float64` - `TotalDuration float64` Total duration in milliseconds - `Outline ResearchEventCompleteDataMetadataOutline` Report outline from research writer - `DirectAnswer string` - `KeyTakeaways []string` - `Outline string` - `RelevantSourceIDs []string` - `URLSources ResearchEventCompleteDataMetadataURLSources` - `ExtractedLinks float64` - `SearchResults float64` - `UserProvided float64` - `Report string` - `Timestamp float64` - `Event Complete` - `const CompleteComplete Complete = "complete"` - `type ResearchEventError struct{…}` Envelope for the "error" event from /v1/research. - `Data ResearchEventErrorData` error - Research failed - `Error ResearchEventErrorDataError` - `Message string` - `Name string` - `Stack string` - `Message string` - `Timestamp float64` - `Activity string` Activity types for research workflow - `const ResearchEventErrorDataActivityPrefetching ResearchEventErrorDataActivity = "prefetching"` - `const ResearchEventErrorDataActivityPlanning ResearchEventErrorDataActivity = "planning"` - `const ResearchEventErrorDataActivityIteration ResearchEventErrorDataActivity = "iteration"` - `const ResearchEventErrorDataActivitySearching ResearchEventErrorDataActivity = "searching"` - `const ResearchEventErrorDataActivityAnalyzing ResearchEventErrorDataActivity = "analyzing"` - `const ResearchEventErrorDataActivityFollowing ResearchEventErrorDataActivity = "following"` - `const ResearchEventErrorDataActivityEvaluating ResearchEventErrorDataActivity = "evaluating"` - `const ResearchEventErrorDataActivityOutlining ResearchEventErrorDataActivity = "outlining"` - `const ResearchEventErrorDataActivityWriting ResearchEventErrorDataActivity = "writing"` - `const ResearchEventErrorDataActivityJudging ResearchEventErrorDataActivity = "judging"` - `Iteration float64` - `Event Error` - `const ErrorError Error = "error"` - `type ResearchEventEvaluatingEnd struct{…}` Envelope for the "evaluating:end" event from /v1/research. - `Data ResearchEventEvaluatingEndData` - `Coverage string` - `const ResearchEventEvaluatingEndDataCoverageLight ResearchEventEvaluatingEndDataCoverage = "Light"` - `const ResearchEventEvaluatingEndDataCoverageModerate ResearchEventEvaluatingEndDataCoverage = "Moderate"` - `const ResearchEventEvaluatingEndDataCoverageSolid ResearchEventEvaluatingEndDataCoverage = "Solid"` - `const ResearchEventEvaluatingEndDataCoverageComprehensive ResearchEventEvaluatingEndDataCoverage = "Comprehensive"` - `Gaps string` - `Iteration float64` - `Message string` - `NextQueries []string` - `QuestionAssessments []ResearchEventEvaluatingEndDataQuestionAssessment` - `Findings string` What we learned (if answered/partial) or what's missing (if unanswered) - `Question string` The research question being assessed - `Status string` Status: answered (clear info), partial (some info, gaps remain), unanswered (no relevant info) - `const ResearchEventEvaluatingEndDataQuestionAssessmentStatusAnswered ResearchEventEvaluatingEndDataQuestionAssessmentStatus = "answered"` - `const ResearchEventEvaluatingEndDataQuestionAssessmentStatusPartial ResearchEventEvaluatingEndDataQuestionAssessmentStatus = "partial"` - `const ResearchEventEvaluatingEndDataQuestionAssessmentStatusUnanswered ResearchEventEvaluatingEndDataQuestionAssessmentStatus = "unanswered"` - `ShouldContinue bool` - `Timestamp float64` - `Event EvaluatingEnd` - `const EvaluatingEndEvaluatingEnd EvaluatingEnd = "evaluating:end"` - `type ResearchEventEvaluatingStart struct{…}` Envelope for the "evaluating:start" event from /v1/research. - `Data ResearchEventEvaluatingStartData` - `Iteration float64` - `Message string` - `PagesAnalyzed float64` Total pages analyzed so far (including this iteration) - `QuestionCount float64` Number of research questions being assessed - `Timestamp float64` - `Event EvaluatingStart` - `const EvaluatingStartEvaluatingStart EvaluatingStart = "evaluating:start"` - `type ResearchEventFollowingEnd struct{…}` Envelope for the "following:end" event from /v1/research. - `Data ResearchEventFollowingEndData` - `Failed float64` - `Followed float64` - `Iteration float64` - `Message string` - `Samples []ResearchEventFollowingEndDataSample` - `Domain string` - `Title string` - `URL string` - `URLSource string` URL source tracking - where a URL came from - `const ResearchEventFollowingEndDataSampleURLSourceUserInput ResearchEventFollowingEndDataSampleURLSource = "user-input"` - `const ResearchEventFollowingEndDataSampleURLSourceSearchResult ResearchEventFollowingEndDataSampleURLSource = "search-result"` - `const ResearchEventFollowingEndDataSampleURLSourceExtractedLink ResearchEventFollowingEndDataSampleURLSource = "extracted-link"` - `Relevance string` - `const ResearchEventFollowingEndDataSampleRelevanceLow ResearchEventFollowingEndDataSampleRelevance = "low"` - `const ResearchEventFollowingEndDataSampleRelevanceMedium ResearchEventFollowingEndDataSampleRelevance = "medium"` - `const ResearchEventFollowingEndDataSampleRelevanceHigh ResearchEventFollowingEndDataSampleRelevance = "high"` - `Reliability string` - `const ResearchEventFollowingEndDataSampleReliabilityLow ResearchEventFollowingEndDataSampleReliability = "low"` - `const ResearchEventFollowingEndDataSampleReliabilityMedium ResearchEventFollowingEndDataSampleReliability = "medium"` - `const ResearchEventFollowingEndDataSampleReliabilityHigh ResearchEventFollowingEndDataSampleReliability = "high"` - `Summary string` - `Timestamp float64` - `Event FollowingEnd` - `const FollowingEndFollowingEnd FollowingEnd = "following:end"` - `type ResearchEventFollowingStart struct{…}` Envelope for the "following:start" event from /v1/research. - `Data ResearchEventFollowingStartData` - `Iteration float64` - `LinkCount float64` - `Message string` - `Timestamp float64` - `Event FollowingStart` - `const FollowingStartFollowingStart FollowingStart = "following:start"` - `type ResearchEventIterationEnd struct{…}` Envelope for the "iteration:end" event from /v1/research. - `Data ResearchEventIterationEndData` - `IsLast bool` Whether this is the final iteration - `Iteration float64` - `Message string` - `Timestamp float64` - `StopReason string` Why research iterations stopped (only present when isLast is true) - `const ResearchEventIterationEndDataStopReasonMaxIterations ResearchEventIterationEndDataStopReason = "max_iterations"` - `const ResearchEventIterationEndDataStopReasonCoverageSufficient ResearchEventIterationEndDataStopReason = "coverage_sufficient"` - `Event IterationEnd` - `const IterationEndIterationEnd IterationEnd = "iteration:end"` - `type ResearchEventIterationStart struct{…}` Envelope for the "iteration:start" event from /v1/research. - `Data ResearchEventIterationStartData` - `Iteration float64` - `MaxIterations float64` Maximum iterations for this research mode - `Message string` - `Queries []string` Search queries to execute in this iteration - `Timestamp float64` - `Event IterationStart` - `const IterationStartIterationStart IterationStart = "iteration:start"` - `type ResearchEventJudgingEnd struct{…}` Envelope for the "judging:end" event from /v1/research. - `Data ResearchEventJudgingEndData` - `Approved bool` - `Attempt float64` - `Message string` - `Score float64` - `Timestamp float64` - `Feedback string` - `Event JudgingEnd` - `const JudgingEndJudgingEnd JudgingEnd = "judging:end"` - `type ResearchEventJudgingStart struct{…}` Envelope for the "judging:start" event from /v1/research. - `Data ResearchEventJudgingStartData` - `Attempt float64` - `MaxAttempts float64` Maximum attempts allowed (1 + maxRevisions) - `Message string` - `Timestamp float64` - `Event JudgingStart` - `const JudgingStartJudgingStart JudgingStart = "judging:start"` - `type ResearchEventOutliningEnd struct{…}` Envelope for the "outlining:end" event from /v1/research. - `Data ResearchEventOutliningEndData` - `Message string` - `SourcesSelected float64` - `Timestamp float64` - `Event OutliningEnd` - `const OutliningEndOutliningEnd OutliningEnd = "outlining:end"` - `type ResearchEventOutliningStart struct{…}` Envelope for the "outlining:start" event from /v1/research. - `Data ResearchEventOutliningStartData` - `Message string` - `PagesAnalyzed float64` Total pages analyzed across all iterations - `QualityPageCount float64` Pages that meet quality threshold (medium+ relevance and reliability) - `Timestamp float64` - `Event OutliningStart` - `const OutliningStartOutliningStart OutliningStart = "outlining:start"` - `type ResearchEventPlanningEnd struct{…}` Envelope for the "planning:end" event from /v1/research. - `Data ResearchEventPlanningEndData` - `Complexity string` - `const ResearchEventPlanningEndDataComplexitySimple ResearchEventPlanningEndDataComplexity = "simple"` - `const ResearchEventPlanningEndDataComplexityModerate ResearchEventPlanningEndDataComplexity = "moderate"` - `const ResearchEventPlanningEndDataComplexityComplex ResearchEventPlanningEndDataComplexity = "complex"` - `Message string` - `Objective string` - `Plan string` - `Queries []string` - `Questions []string` - `Timestamp float64` - `Event PlanningEnd` - `const PlanningEndPlanningEnd PlanningEnd = "planning:end"` - `type ResearchEventPlanningStart struct{…}` Envelope for the "planning:start" event from /v1/research. - `Data ResearchEventPlanningStartData` - `HasPrefetchedContext bool` Whether prefetched user-provided URLs exist for context - `Message string` - `Timestamp float64` - `Event PlanningStart` - `const PlanningStartPlanningStart PlanningStart = "planning:start"` - `type ResearchEventPrefetchingEnd struct{…}` Envelope for the "prefetching:end" event from /v1/research. - `Data ResearchEventPrefetchingEndData` - `Failed float64` - `Fetched float64` - `Message string` - `Timestamp float64` - `Event PrefetchingEnd` - `const PrefetchingEndPrefetchingEnd PrefetchingEnd = "prefetching:end"` - `type ResearchEventPrefetchingStart struct{…}` Envelope for the "prefetching:start" event from /v1/research. - `Data ResearchEventPrefetchingStartData` - `Message string` - `Timestamp float64` - `URLCount float64` - `URLs []string` - `Event PrefetchingStart` - `const PrefetchingStartPrefetchingStart PrefetchingStart = "prefetching:start"` - `type ResearchEventSearchingEnd struct{…}` Envelope for the "searching:end" event from /v1/research. - `Data ResearchEventSearchingEndData` - `Iteration float64` - `Message string` - `Timestamp float64` - `URLsFound float64` - `URLsNew float64` - `Event SearchingEnd` - `const SearchingEndSearchingEnd SearchingEnd = "searching:end"` - `type ResearchEventSearchingStart struct{…}` Envelope for the "searching:start" event from /v1/research. - `Data ResearchEventSearchingStartData` - `Iteration float64` - `Message string` - `Queries []string` - `Timestamp float64` - `Event SearchingStart` - `const SearchingStartSearchingStart SearchingStart = "searching:start"` - `type ResearchEventStart struct{…}` Envelope for the "start" event from /v1/research. - `Data ResearchEventStartData` start - Research begins - `Message string` - `Timestamp float64` - `Event Start` - `const StartStart Start = "start"` - `type ResearchEventWritingEnd struct{…}` Envelope for the "writing:end" event from /v1/research. - `Data ResearchEventWritingEndData` - `Attempt float64` - `Message string` - `Timestamp float64` - `Event WritingEnd` - `const WritingEndWritingEnd WritingEnd = "writing:end"` - `type ResearchEventWritingStart struct{…}` Envelope for the "writing:start" event from /v1/research. - `Data ResearchEventWritingStartData` - `Attempt float64` - `IsRevision bool` Whether this is a revision attempt (attempt > 1) - `MaxAttempts float64` Maximum attempts allowed (1 + maxRevisions) - `Message string` - `Timestamp float64` - `PreviousScore float64` Previous judgment score if this is a revision - `Event WritingStart` - `const WritingStartWritingStart WritingStart = "writing:start"` ### Example ```go package main import ( "context" "fmt" "github.com/stainless-sdks/tabstack-go" "github.com/stainless-sdks/tabstack-go/option" ) func main() { client := tabstack.NewClient( option.WithAPIKey("My API Key"), ) stream := client.Agent.ResearchStreaming(context.TODO(), tabstack.AgentResearchParams{ Query: "What are the latest developments in quantum computing?", }) for stream.Next() { fmt.Printf("%+v\n", stream.Current()) } err := stream.Err() if err != nil { panic(err.Error()) } } ```