Skip to main content
Specialized models

Deep research (Qwen-Deep-Research)

Automates complex research through planning, multiple rounds of web searches, and structured report generation. Gathers and synthesizes information without manual effort.

Deep research (Qwen-Deep-Research) plans research steps, runs multiple rounds of web searches, integrates what it finds, and produces a structured research report. Supported regions: China North 2 (Beijing) only. Use an API key created in that region and https://cn-beijing.maas.qwencloudapi.com. See Regions and endpoints.

Getting started

Create an API key and export it as an environment variable. If you use the DashScope SDK, install the SDK first. The model uses a two-step workflow: a follow-up question step, where the model clarifies your research scope, and a deep research step, where the model searches, analyzes, and generates a report. The follow-up question lets the model understand exactly what to investigate before starting a lengthy research run.
The model currently supports only the Python edition of the DashScope SDK and direct DashScope HTTP calls. It does not support the Java edition of the DashScope SDK or OpenAI-compatible calls.
import os
import dashscope

# China North 2 (Beijing). This model is available only in this region.
# API keys are region-specific. See https://docs.qwencloud.com/developer-guides/getting-started/regions
dashscope.base_http_api_url = "https://cn-beijing.maas.qwencloudapi.com/api/v1"

API_KEY = os.getenv("DASHSCOPE_API_KEY")


def call_deep_research_model(messages, step_name):
  print(f"\n=== {step_name} ===")
  current_phase = None
  phase_content = ""
  references = []
  research_goal = ""
  web_sites = []

  responses = dashscope.Generation.call(
    api_key=API_KEY,
    model="qwen-deep-research",
    messages=messages,
    # Only streaming output is supported.
    stream=True,
  )

  for response in responses:
    if hasattr(response, "status_code") and response.status_code != 200:
      print(f"HTTP status: {response.status_code}, code: {response.code}, message: {response.message}")
      break

    message = response.output.get("message", {})
    phase = message.get("phase")
    content = message.get("content", "")
    status = message.get("status")
    extra = message.get("extra", {})

    # A new phase started.
    if phase != current_phase:
      if current_phase and phase_content:
        print(f"\n{current_phase} phase completed")
      current_phase = phase
      phase_content = ""
      print(f"\nEntering {phase} phase")

    # References are delivered in the answer phase.
    if phase == "answer":
      new_references = extra.get("deep_research", {}).get("references")
      if new_references and new_references != references:
        references = new_references
        print(f"\nReferences ({len(references)}):")
        for i, ref in enumerate(references, 1):
          print(f"  {i}. {ref.get('title')} - {ref.get('url')}")

    # Web search progress is delivered in the WebResearch phase.
    if phase == "WebResearch":
      research = extra.get("deep_research", {}).get("research", {})
      if status in ("streamingQueries", "streamingThinking") and research.get("researchGoal"):
        research_goal += research["researchGoal"]
        print(f"\nResearch goal: {research_goal}", end="", flush=True)
      elif status == "streamingWebResult" and research.get("webSites"):
        sites = research["webSites"]
        if sites != web_sites:
          web_sites = sites
          print(f"\nFound {len(sites)} relevant websites:")
          for i, site in enumerate(sites, 1):
            print(f"  {i}. {site.get('title')} - {site.get('url')}")

    if content:
      phase_content += content
      print(content, end="", flush=True)

    if status == "finished":
      usage = response.usage or {}
      print(f"\n\nTokens: {usage.get('input_tokens', 0)} input, {usage.get('output_tokens', 0)} output")

    # KeepAlive only keeps the connection open between long-running steps.
    if phase == "KeepAlive":
      continue

  return phase_content


def main():
  # Step 1: the model asks clarifying questions about the research scope.
  messages = [{"role": "user", "content": "Research the application of AI in education"}]
  step1_content = call_deep_research_model(messages, "Step 1: Clarifying questions")

  # Step 2: answer the clarifying questions and start the research.
  messages = [
    {"role": "user", "content": "Research the application of AI in education"},
    {"role": "assistant", "content": step1_content},
    {"role": "user", "content": "I mainly focus on personalized learning and intelligent assessment"},
  ]
  call_deep_research_model(messages, "Step 2: Deep research")


if __name__ == "__main__":
  main()

Supported models

ModelContext windowMax inputMax output
qwen-deep-research1,000,000997,95232,768
qwen-deep-research is the mainline model and is continuously updated.

Core capabilities

Track progress with two response fields: phase is the current task, and status is the progress within that task. Clarifying questions and report generation (phase: "answer") Analyzes the query, asks clarifying questions to define the scope, and generates the final research report.
  • typing: Generating text content
  • finished: Text content generation completed
Research planning (phase: "ResearchPlanning") Creates a research outline from the query.
  • typing: Generating the research plan
  • finished: Research plan completed
Web search (phase: "WebResearch") Runs multiple rounds of web searches and content analysis. WebResultFinished marks the end of each round; finished marks the end of the phase.
  • streamingQueries: Generating research goals and search queries
  • streamingWebResult: Running searches and analyzing web content
  • WebResultFinished: One search round completed
  • finished: Web search phase completed
Connection keepalive (phase: "KeepAlive") Keeps the connection open between long-running tasks. Ignore this phase and continue reading the stream.

Image input

The model accepts images and incorporates them into the research. Set content to an array that mixes image and text objects.
  • Supported formats: JPEG, PNG, BMP, WEBP. Up to 10 MB per image.
  • Up to 5 images per request. Public URLs and Base64 encoding are supported.
  • The response format is identical to text-only requests. The model generates a report based on the image content.
import os
import dashscope

# China North 2 (Beijing). This model is available only in this region.
# API keys are region-specific. See https://docs.qwencloud.com/developer-guides/getting-started/regions
dashscope.base_http_api_url = "https://cn-beijing.maas.qwencloudapi.com/api/v1"

messages = [
  {
    "role": "user",
    "content": [
      {"image": "https://example.com/chart.png"},
      {"text": "Analyze the data trends in this chart and research the key findings in depth"}
    ]
  }
]

responses = dashscope.Generation.call(
  api_key=os.getenv("DASHSCOPE_API_KEY"),
  model="qwen-deep-research",
  messages=messages,
  stream=True,
)

for response in responses:
  content = response.output.get("message", {}).get("content", "")
  if content:
    print(content, end="", flush=True)

Billing

ModelInput cost (per 1M tokens)Output cost (per 1M tokens)Free quota
qwen-deep-research$7.742$23.367None
Billing counts input tokens (user messages and system prompts) and output tokens (clarifying questions, research plans, goals, search queries, and the final report).

Going live

Stream the response The model supports only streaming output (stream=True). A single research task can run for several minutes across dozens of search-and-read cycles, which exceeds the timeout of a synchronous request. Keep the connection open and track progress with phase and status. Handle errors per chunk Check the status code of each chunk. For non-200 status codes, read code and message and handle them appropriately. Monitor token usage When status is finished, read token usage from response.usage. Skip the KeepAlive phase KeepAlive only keeps the connection open between long-running tasks. Ignore it and continue reading the stream.

FAQ

Why is content empty for some chunks? Early chunks carry metadata only. Content arrives in later chunks as the model generates it. How do I know a phase is complete? A phase completes when status becomes finished. Does the model support OpenAI-compatible calls? No. Use the DashScope SDK or direct DashScope HTTP calls. How do I skip the clarifying questions and start research immediately? Set enable_feedback to false in parameters. How do I pass images? Set content to an array of image and text objects. The model supports image input.
Deep research (Qwen-Deep-Research) - QwenCloud