Skip to main content
The DeepResearch API performs comprehensive research by searching multiple sources, analyzing content, and generating detailed reports. Unlike synchronous APIs, tasks run in the background, enabling thorough multi-step research that can take several minutes.
DeepResearch is ideal for complex research tasks. For quick answers to simple questions, use the Answer API instead.

Basic Usage

import { Valyu } from "valyu-js";

const valyu = new Valyu();

// Create a research task
const task = await valyu.deepresearch.create({
  input: "What are the key differences between RAG and fine-tuning for LLMs?",
  model: "lite"
});

if (task.success) {
  console.log(`Task created: ${task.deepresearch_id}`);
  
  // Wait for completion with progress updates
  const result = await valyu.deepresearch.wait(task.deepresearch_id, {
    onProgress: (s) => console.log(`Status: ${s.status}`)
  });
  
  if (result.status === "completed") {
    console.log(result.output);
    console.log(`Cost: $${result.usage?.total_cost.toFixed(4)}`);
  }
}

Research Modes

DeepResearch offers two modes optimized for different use cases:
ModeBest ForTypical Completion Time
liteQuick research, fact-checking, straightforward questions5-10 minutes
heavyComplex analysis, multi-faceted topics, detailed reports15-30 minutes
// Use heavy mode for complex research
const task = await valyu.deepresearch.create({
  input: "Analyze the competitive landscape of cloud computing in 2024",
  model: "heavy"
});

Parameters

Input (Required)

ParameterTypeDescription
inputstringResearch query or task description

Options (Optional)

ParameterTypeDescriptionDefault
model"lite" | "heavy"Research mode"lite"
outputFormatsarrayOutput formats (see below)["markdown"]
strategystringNatural language strategy instructionsundefined
searchobjectSearch configuration (filters, date range)undefined
urlsstring[]URLs to analyze (max 10)undefined
filesFileAttachment[]File attachments (max 10)undefined
mcpServersMCPServerConfig[]MCP server configurations (max 5)undefined
codeExecutionbooleanEnable code executiontrue
previousReportsstring[]Previous task IDs for context (max 3)undefined
webhookUrlstringHTTPS URL for completion notificationundefined
metadataobjectCustom metadata for trackingundefined

Output Formats

Markdown (Default)

const task = await valyu.deepresearch.create({
  input: "Explain quantum computing advancements in 2024",
  outputFormats: ["markdown"]
});

Markdown + PDF

Request both markdown and a downloadable PDF report:
const task = await valyu.deepresearch.create({
  input: "Write a report on renewable energy trends",
  outputFormats: ["markdown", "pdf"]
});

const result = await valyu.deepresearch.wait(task.deepresearch_id!);

if (result.pdf_url) {
  console.log(`PDF available at: ${result.pdf_url}`);
}

Structured JSON

Get research results in a custom schema using JSON Schema specification:
const task = await valyu.deepresearch.create({
  input: "Research competitor pricing in the SaaS market",
  outputFormats: [{
    type: "object",
    properties: {
      competitors: {
        type: "array",
        items: {
          type: "object",
          properties: {
            name: { type: "string" },
            pricing_model: { type: "string" },
            price_range: { type: "string" },
            key_features: {
              type: "array",
              items: { type: "string" }
            }
          },
          required: ["name", "pricing_model"]
        }
      },
      market_summary: { type: "string" },
      recommendations: {
        type: "array",
        items: { type: "string" }
      }
    },
    required: ["competitors", "market_summary"]
  }]
});

const result = await valyu.deepresearch.wait(task.deepresearch_id!);

if (result.output_type === "json") {
  const data = result.output as any;
  data.competitors.forEach((competitor: any) => {
    console.log(`${competitor.name}: ${competitor.pricing_model}`);
  });
}
You cannot mix JSON Schema with markdown/pdf formats. Use one or the other.
The schema must be a valid JSON Schema. Use type, properties, required, items, and other standard JSON Schema keywords.

Search Configuration

Filter which sources the research uses:
const task = await valyu.deepresearch.create({
  input: "Latest AI research in healthcare diagnostics",
  model: "heavy",
  search: {
    searchType: "all",  // "all", "web", or "proprietary"
    includedSources: ["pubmed", "arxiv", "nature.com"]
  }
});

Search Types

TypeDescription
allSearch web and proprietary sources (default)
webWeb sources only
proprietaryAcademic and premium sources only

File Attachments

Analyze documents as part of research:
import * as fs from "fs";

// Read and encode a PDF
const pdfBuffer = fs.readFileSync("report.pdf");
const pdfData = pdfBuffer.toString("base64");

const task = await valyu.deepresearch.create({
  input: "Summarize the key findings and compare with market trends",
  model: "heavy",
  files: [{
    data: `data:application/pdf;base64,${pdfData}`,
    filename: "report.pdf",
    mediaType: "application/pdf",
    context: "Q4 2024 financial report"  // Optional context
  }]
});
Supported file types: PDFs, images (PNG, JPEG, WebP), and documents.

URL Extraction

Include specific URLs to analyze alongside web research:
const task = await valyu.deepresearch.create({
  input: "Compare the approaches described in these articles",
  urls: [
    "https://example.com/article-1",
    "https://example.com/article-2"
  ]
});

Waiting for Completion

Basic Wait

const result = await valyu.deepresearch.wait(task.deepresearch_id!);

if (result.status === "completed") {
  console.log(result.output);
}

With Progress Callback

Track research progress in real-time:
const result = await valyu.deepresearch.wait(task.deepresearch_id!, {
  pollInterval: 5000,      // Check every 5 seconds
  maxWaitTime: 900000,     // Timeout after 15 minutes (lite mode)
  onProgress: (status) => {
    if (status.progress) {
      const pct = (status.progress.current_step / status.progress.total_steps) * 100;
      console.log(`Progress: ${pct.toFixed(0)}% - Step ${status.progress.current_step}/${status.progress.total_steps}`);
    }
    console.log(`Status: ${status.status}`);
  }
});

Polling Options

OptionTypeDescriptionDefault
pollIntervalnumberMilliseconds between status checks5000
maxWaitTimenumberMaximum wait time in milliseconds3600000
onProgressfunctionCallback for progress updatesundefined

Response Format

interface DeepResearchStatusResponse {
  success: boolean;
  deepresearch_id?: string;
  status?: "queued" | "running" | "completed" | "failed" | "cancelled";
  query?: string;
  mode?: "lite" | "heavy";
  output_type?: "markdown" | "json";
  output?: string | Record<string, any>;  // Research output
  sources?: DeepResearchSource[];  // Sources used
  usage?: DeepResearchUsage;  // Cost breakdown
  completed_at?: number;  // Unix timestamp
  pdf_url?: string;  // PDF download URL
  images?: ImageMetadata[];  // Generated images
  error?: string;  // Error message if failed
}

Source Object

interface DeepResearchSource {
  title: string;
  url: string;
  snippet?: string;
  source?: string;  // web, pubmed, arxiv, etc.
  word_count?: number;
  doi?: string;  // For academic papers
}

Usage Object

interface DeepResearchUsage {
  search_cost: number;   // Search operations
  contents_cost: number; // Content retrieval
  ai_cost: number;       // AI processing
  compute_cost: number;  // Compute resources
  total_cost: number;    // Total billed
}

Task Management

Check Status

const status = await valyu.deepresearch.status(taskId);

console.log(`Status: ${status.status}`);
if (status.progress) {
  console.log(`Step ${status.progress.current_step}/${status.progress.total_steps}`);
}

Add Follow-up Instructions

Add instructions to a running task:
const response = await valyu.deepresearch.update(
  taskId,
  "Focus more on peer-reviewed sources from 2024"
);

if (response.success) {
  console.log("Instruction added");
}

Cancel a Task

const response = await valyu.deepresearch.cancel(taskId);

if (response.success) {
  console.log("Task cancelled");
}

Delete a Task

const response = await valyu.deepresearch.delete(taskId);

if (response.success) {
  console.log("Task deleted");
}

List All Tasks

const tasks = await valyu.deepresearch.list({
  apiKeyId: "your-api-key-id",
  limit: 50
});

tasks.data?.forEach(task => {
  console.log(`${task.query?.substring(0, 50)}... - ${task.status}`);
});

Webhooks

Get notified when research completes instead of polling. Webhooks are ideal for production systems and serverless architectures.

Setup

const task = await valyu.deepresearch.create({
  input: "Research market trends in AI",
  webhookUrl: "https://your-app.com/webhooks/deepresearch"  // Must be HTTPS
});

// IMPORTANT: Save the secret immediately - it's only returned once
const webhookSecret = task.webhook_secret;
console.log(`Store securely: ${webhookSecret}`);
The webhook_secret is only returned once. Store it securely—you cannot retrieve it later.

Verifying Signatures

Always verify webhook signatures to ensure authenticity:
import crypto from "crypto";

function verifyWebhook(
  payloadBody: string,
  signatureHeader: string,
  timestampHeader: string,
  secret: string
): boolean {
  // Reconstruct signed payload: timestamp.payload
  const signedPayload = `${timestampHeader}.${payloadBody}`;
  
  // Generate expected signature
  const expectedSignature = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(signedPayload)
    .digest("hex");
  
  // Constant-time comparison prevents timing attacks
  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature),
    Buffer.from(signatureHeader)
  );
}

Handling Webhooks

import express from "express";

const app = express();
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!;

app.post(
  "/webhooks/deepresearch",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.headers["x-webhook-signature"] as string;
    const timestamp = req.headers["x-webhook-timestamp"] as string;
    const payload = req.body.toString();
    
    if (!verifyWebhook(payload, signature, timestamp, WEBHOOK_SECRET)) {
      return res.status(401).json({ error: "Invalid signature" });
    }
    
    const data = JSON.parse(payload);
    
    if (data.status === "completed") {
      // Process completed research
      saveResearchResult(data.deepresearch_id, data.output);
    } else if (data.status === "failed") {
      // Handle failure
      logError(data.deepresearch_id, data.error);
    }
    
    return res.status(200).json({ received: true });
  }
);

Webhook Headers

HeaderDescription
X-Webhook-SignatureHMAC-SHA256 signature: sha256=<hex>
X-Webhook-TimestampUnix timestamp (ms) when sent
Content-Typeapplication/json

Retry Behavior

  • 5 retry attempts with exponential backoff (1s → 2s → 4s → 8s → 16s)
  • 15 second timeout per request
  • 4xx errors: No retry (client error)
  • 5xx errors: Will retry (server error)
Return a 2xx status quickly and process the payload asynchronously to avoid timeouts.

Use Case Examples

Academic Research Assistant

const task = await valyu.deepresearch.create({
  input: "Analyze recent advances in transformer architectures for NLP",
  model: "heavy",
  search: {
    searchType: "proprietary",
    includedSources: ["arxiv", "pubmed", "nature.com"]
  },
  strategy: "Focus on peer-reviewed sources. Include methodology comparisons and performance benchmarks."
});

const result = await valyu.deepresearch.wait(task.deepresearch_id!);

// Access academic sources
result.sources?.forEach(source => {
  if (source.doi) {
    console.log(`📄 ${source.title}`);
    console.log(`   DOI: ${source.doi}`);
  }
});

Competitive Intelligence

interface CompetitiveAnalysis {
  market_overview: string;
  key_players: Array<{
    company: string;
    market_share: string;
    strengths: string[];
    weaknesses: string[];
  }>;
  trends: string[];
  opportunities: string[];
}

const task = await valyu.deepresearch.create({
  input: "Analyze the competitive landscape of the cloud computing market",
  model: "heavy",
  outputFormats: [{
    type: "object",
    properties: {
      market_overview: { type: "string" },
      key_players: {
        type: "array",
        items: {
          type: "object",
          properties: {
            company: { type: "string" },
            market_share: { type: "string" },
            strengths: { type: "array", items: { type: "string" } },
            weaknesses: { type: "array", items: { type: "string" } }
          }
        }
      },
      trends: { type: "array", items: { type: "string" } },
      opportunities: { type: "array", items: { type: "string" } }
    },
    required: ["market_overview", "key_players", "trends"]
  }]
});

const result = await valyu.deepresearch.wait(task.deepresearch_id!);

if (result.output_type === "json") {
  const analysis = result.output as CompetitiveAnalysis;
  console.log(`Market Overview: ${analysis.market_overview}\n`);
  
  analysis.key_players.forEach(player => {
    console.log(`📊 ${player.company} (${player.market_share})`);
    console.log(`   Strengths: ${player.strengths.slice(0, 3).join(", ")}`);
  });
}

Document Analysis

import * as fs from "fs";

// Load multiple documents
const files = ["q1_report.pdf", "q2_report.pdf"].map(filename => {
  const buffer = fs.readFileSync(filename);
  const data = buffer.toString("base64");
  return {
    data: `data:application/pdf;base64,${data}`,
    filename,
    mediaType: "application/pdf"
  };
});

const task = await valyu.deepresearch.create({
  input: "Compare performance across these quarterly reports. Identify trends and anomalies.",
  model: "heavy",
  files,
  outputFormats: ["markdown", "pdf"]
});

const result = await valyu.deepresearch.wait(task.deepresearch_id!);
console.log(`Analysis complete. PDF: ${result.pdf_url}`);

Error Handling

const task = await valyu.deepresearch.create({
  input: "Research query"
});

if (!task.success) {
  console.error(`Failed to create task: ${task.error}`);
  return;
}

try {
  const result = await valyu.deepresearch.wait(task.deepresearch_id!, {
    maxWaitTime: 900000  // 15 minutes for lite, use 2700000 for heavy
  });
  
  if (result.status === "completed") {
    console.log(result.output);
  } else if (result.status === "failed") {
    console.error(`Research failed: ${result.error}`);
  }
  
} catch (error: any) {
  if (error.message.includes("Maximum wait time")) {
    console.log("Task timed out - cancelling");
    await valyu.deepresearch.cancel(task.deepresearch_id!);
  } else {
    console.error(`Task error: ${error.message}`);
  }
}

Best Practices

Choose the Right Mode

// Lite: Quick research, simple questions
await valyu.deepresearch.create({
  input: "What is the current market cap of Apple?",
  model: "lite"
});

// Heavy: Complex analysis, detailed reports
await valyu.deepresearch.create({
  input: "Analyze Apple's competitive position vs Microsoft in cloud services",
  model: "heavy"
});

Optimize Polling

// Lite mode: poll every 5 seconds, 15 min timeout
await valyu.deepresearch.wait(taskId, { pollInterval: 5000, maxWaitTime: 900000 });

// Heavy mode: poll every 15 seconds, 45 min timeout
await valyu.deepresearch.wait(taskId, { pollInterval: 15000, maxWaitTime: 2700000 });

// Production: use webhooks instead
await valyu.deepresearch.create({
  input: "...",
  webhookUrl: "https://your-app.com/webhooks"
});

Provide Clear Context

// ❌ Vague
await valyu.deepresearch.create({ input: "Tell me about AI" });

// ✅ Specific with context
await valyu.deepresearch.create({
  input: "What are the practical applications of large language models in healthcare, focusing on diagnostic assistance and clinical documentation?",
  strategy: "Focus on peer-reviewed studies and real-world deployments. Include both benefits and limitations.",
  search: { searchType: "proprietary" }
});

Type-Safe Structured Output

// Define your expected output structure
interface ResearchOutput {
  summary: string;
  key_findings: string[];
  recommendations: string[];
}

const result = await valyu.deepresearch.wait(taskId);

if (result.output_type === "json" && result.output) {
  const data = result.output as ResearchOutput;
  // TypeScript now knows the structure
  console.log(data.summary);
  data.key_findings.forEach(finding => console.log(`- ${finding}`));
}

Handle Long-Running Tasks

async function researchWithStatus(query: string) {
  const task = await valyu.deepresearch.create({
    input: query,
    model: "heavy"
  });
  
  if (!task.success) {
    return null;
  }
  
  console.log(`Started research: ${task.deepresearch_id}`);
  
  try {
    const result = await valyu.deepresearch.wait(task.deepresearch_id!, {
      pollInterval: 10000,
      maxWaitTime: 2700000,  // 45 minutes for heavy mode
      onProgress: (s) => console.log(`  ${s.status}...`)
    });
    return result;
    
  } catch (error: any) {
    if (error.message.includes("Maximum wait time")) {
      // Check if still running
      const status = await valyu.deepresearch.status(task.deepresearch_id!);
      if (status.status === "running") {
        console.log("Still running - check back later");
        return status;
      }
    }
    throw error;
  }
}

const result = await researchWithStatus("Comprehensive market analysis of EV industry");