Qwen Cloud Developer Tutorial
A hands-on guide to building with Qwen Cloud — from your first API call to advanced features like vision, image generation, function calling, and thinking mode.
What is Qwen Cloud?
Qwen Cloud provides API access to Qwen large language models and multimodal AI. Capabilities include text generation, vision (image & video understanding), image generation, video generation, speech-to-text, text-to-speech, embeddings, and reranking — all via OpenAI-compatible and DashScope endpoints.
What you will learn:
| Section | Topic |
|---|---|
| 1 | Setup & Installation |
| 2 | Your First API Call |
| 3 | Text Generation (Chat Completions) |
| 4 | Streaming Responses |
| 5 | Thinking Mode (Reasoning) |
| 6 | Vision — Image Understanding |
| 7 | Vision — Video Understanding |
| 8 | Image Generation |
| 9 | Function Calling (Tool Use) |
| 10 | Structured Output |
| 11 | Async Requests for High Throughput |
| 12 | Next Steps & Resources |
1. Setup & Installation
1.1 Prerequisites
Before you begin, you will need:
- A Qwen Cloud account — Sign up at home.qwencloud.com
- An API key — Generate one in the Qwen Cloud console
Keep your API key secret! Never commit it to version control or share it publicly.
1.2 Install the OpenAI Python SDK
Qwen Cloud is fully compatible with the OpenAI SDK, so you can use the same openai package you may already be familiar with.
# Install the OpenAI Python SDK
!pip install -q openai1.3 Configure Your API Key
Set your API key as an environment variable. You can either:
Option A: Export it in your terminal before launching Jupyter:
export DASHSCOPE_API_KEY="sk-your-api-key-here"Option B: Set it directly in this notebook (for quick testing only — do not commit this!):
import os
# Option A: Read from environment (recommended)
# Make sure you have run: export DASHSCOPE_API_KEY="sk-your-api-key-here"
# Option B: Set directly (for quick testing only — never commit this!)
# os.environ["DASHSCOPE_API_KEY"] = "sk-your-api-key-here"
# Verify the key is set
api_key = os.getenv("DASHSCOPE_API_KEY")
if api_key:
print(f"API key configured (starts with {api_key[:6]}...)")
else:
print("API key not found. Please set DASHSCOPE_API_KEY.")1.4 Initialize the Client
The key difference from standard OpenAI usage is the base_url — it points to the Qwen Cloud endpoint.
from openai import OpenAI
# Qwen Cloud uses the OpenAI-compatible endpoint
client = OpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
)
print("Client initialized successfully!")1.5 Available Models
Qwen Cloud offers a range of models. Here are the key ones for text generation:
| Model | Best For | Speed | Cost |
|---|---|---|---|
qwen3.6-max-preview |
Complex reasoning & coding | Slower | Higher |
qwen3.6-plus |
Balanced performance | Medium | Medium |
qwen3.6-flash |
Fast & cost-effective | Fast | Lower |
All models share the same API — just change the model parameter. Start with qwen3.6-plus for a good balance of quality and speed.
2. Your First API Call
Let us make a simple request to verify everything is working.
completion = client.chat.completions.create(
model="qwen3.6-plus",
messages=[
{"role": "user", "content": "Hello! Tell me a fun fact about AI."}
]
)
print(completion.choices[0].message.content)If you see a response, congratulations — you are connected to Qwen Cloud!
Let us look at the full response structure:
import json
# Inspect the full API response
print(json.dumps(json.loads(completion.model_dump_json()), indent=2))Key fields in the response:
choices[0].message.content— The model text responsechoices[0].finish_reason— Why the model stopped ("stop"= natural completion)usage.prompt_tokens/usage.completion_tokens— Token counts for billing
3. Text Generation (Chat Completions)
The Chat Completions API is the primary way to generate text. Requests are composed of three message roles:
- System — Sets the assistant behavior and persona
- User — Your input / prompt
- Assistant — The model response
3.1 Using System Messages
completion = client.chat.completions.create(
model="qwen3.6-plus",
messages=[
{
"role": "system",
"content": "You are a helpful assistant. Answer clearly and concisely."
},
{
"role": "user",
"content": "Summarize the benefits of solar energy in three bullet points."
}
]
)
print(completion.choices[0].message.content)3.2 Multi-Turn Conversations
To maintain context across turns, include the full message history in each request.
messages = [
{"role": "system", "content": "You are a knowledgeable science tutor."},
{"role": "user", "content": "What is photosynthesis?"},
]
# First turn
response1 = client.chat.completions.create(model="qwen3.6-plus", messages=messages)
assistant_reply = response1.choices[0].message.content
print("Turn 1:", assistant_reply)
print("---")
# Add assistant reply and ask a follow-up
messages.append({"role": "assistant", "content": assistant_reply})
messages.append({"role": "user", "content": "Can you explain the light-dependent reactions in simpler terms?"})
# Second turn
response2 = client.chat.completions.create(model="qwen3.6-plus", messages=messages)
print("Turn 2:", response2.choices[0].message.content)3.3 Controlling Temperature & Top-p
The temperature and top_p parameters control how creative or predictable the output is:
| Scenario | Temperature | Top-p |
|---|---|---|
| Creative writing | 0.8-1.0 | 0.9-0.95 |
| Code generation | 0.0-0.3 | 0.7-0.8 |
| Factual Q&A | 0.0-0.3 | 0.5-0.7 |
| Translation | 0.0-0.3 | 0.7-0.8 |
How temperature works: A higher temperature flattens the token probability distribution, making low-probability tokens more likely (more random). A lower temperature sharpens it, favoring high-probability tokens (more predictable).
How top-p works: Top-p sampling selects from the smallest set of tokens whose cumulative probability exceeds a threshold. A higher top_p considers more tokens (more diverse), while a lower top_p considers fewer (more focused).
# Creative mode — high temperature
creative = client.chat.completions.create(
model="qwen3.6-plus",
messages=[{"role": "user", "content": "Write a three-sentence story about a cat and sunlight."}],
temperature=0.9,
top_p=0.95
)
print("Creative:")
print(creative.choices[0].message.content)
print()
# Precise mode — low temperature
precise = client.chat.completions.create(
model="qwen3.6-plus",
messages=[{"role": "user", "content": "Write a three-sentence story about a cat and sunlight."}],
temperature=0.1,
top_p=0.7
)
print("Precise:")
print(precise.choices[0].message.content)4. Streaming Responses
For a better user experience, you can stream responses token by token. This is especially useful for long outputs or chat interfaces.
stream = client.chat.completions.create(
model="qwen3.6-plus",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain quantum computing in simple terms."}
],
stream=True # Enable streaming
)
print("Streaming response:")
full_response = ""
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
full_response += content
print(content, end="", flush=True)
print("\n\n--- Stream complete ---")5. Thinking Mode (Reasoning)
Thinking mode exposes the model internal reasoning process. It is invaluable for complex math, logic, and coding tasks. When enabled, the model returns:
- Phase 1: Thinking —
reasoning_contentshowing step-by-step reasoning - Phase 2: Answer —
contentwith the final response
Enable it by setting enable_thinking to True.
stream = client.chat.completions.create(
model="qwen3.6-plus",
messages=[{"role": "user", "content": "If 3x + 7 = 22, what is x?"}],
extra_body={"enable_thinking": True}, # Enable thinking mode
stream=True
)
thinking_content = ""
answer_content = ""
print("Thinking process:")
for chunk in stream:
if not chunk.choices:
continue
delta = chunk.choices[0].delta
# Phase 1: Reasoning / thinking
if hasattr(delta, "reasoning_content") and delta.reasoning_content:
thinking_content += delta.reasoning_content
print(delta.reasoning_content, end="", flush=True)
# Phase 2: Final answer
if delta.content:
if not answer_content: # Print header on first answer token
print("\n\nAnswer:")
answer_content += delta.content
print(delta.content, end="", flush=True)
print()5.1 Control Thinking Depth with thinking_budget
You can limit how many tokens the model spends on reasoning:
# Limit thinking to 500 tokens (faster, less detailed reasoning)
stream = client.chat.completions.create(
model="qwen3.6-plus",
messages=[{"role": "user", "content": "What is the integral of x^2 from 0 to 5?"}],
extra_body={
"enable_thinking": True,
"thinking_budget": 500 # Max tokens for reasoning
},
stream=True
)
for chunk in stream:
if not chunk.choices:
continue
delta = chunk.choices[0].delta
if hasattr(delta, "reasoning_content") and delta.reasoning_content:
print(delta.reasoning_content, end="", flush=True)
if delta.content:
print(delta.content, end="", flush=True)
print()6. Vision — Image Understanding
Qwen Cloud vision models can analyze images to answer questions, extract text (OCR), describe content, solve visual problems, and even generate code from screenshots.
6.1 Analyze a Single Image from URL
completion = client.chat.completions.create(
model="qwen3.6-plus",
messages=[
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {
"url": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg"
}
},
{
"type": "text",
"text": "Describe what you see in this image."
}
]
}
]
)
print(completion.choices[0].message.content)6.2 Analyze Multiple Images
You can pass multiple images in a single request for comparison or combined analysis.
completion = client.chat.completions.create(
model="qwen3.6-plus",
messages=[
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {
"url": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg"
}
},
{
"type": "image_url",
"image_url": {
"url": "https://dashscope.oss-cn-beijing.aliyuncs.com/images/tiger.png"
}
},
{
"type": "text",
"text": "Compare these two images. What do they depict?"
}
]
}
]
)
print(completion.choices[0].message.content)6.3 Analyze a Local Image (Base64)
For local files, encode them as Base64 and pass them inline.
import base64
def encode_image(image_path: str) -> str:
"""Convert a local image file to a Base64-encoded string."""
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode("utf-8")
# Example usage (uncomment and set your image path):
# base64_image = encode_image("/path/to/your/image.png")
#
# completion = client.chat.completions.create(
# model="qwen3.6-plus",
# messages=[
# {
# "role": "user",
# "content": [
# {
# "type": "image_url",
# "image_url": {"url": f"data:image/png;base64,{base64_image}"}
# },
# {"type": "text", "text": "Describe what you see in this image."}
# ]
# }
# ]
# )
# print(completion.choices[0].message.content)
print("encode_image() helper function is ready to use.")
print("Uncomment the example above and set your image path to try it.")7. Vision — Video Understanding
Qwen Cloud can analyze video content too — summarizing what happens, locating events, and generating descriptions.
completion = client.chat.completions.create(
model="qwen3.6-plus",
messages=[
{
"role": "user",
"content": [
{
"type": "video_url",
"video_url": {
"url": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241115/cqqkru/1.mp4"
},
"fps": 2 # Extract 2 frames per second
},
{
"type": "text",
"text": "Summarize what happens in this video."
}
]
}
]
)
print(completion.choices[0].message.content)Tip: The
fpsparameter controls how many frames per second are extracted from the video. A lowerfpsvalue uses fewer tokens but may miss fast-moving details.
8. Image Generation
Qwen Cloud offers powerful image generation models (Wan series) that can create images from text prompts.
Image generation uses the DashScope native API with asynchronous task polling. Here is the complete workflow:
- Submit an async generation task
- Poll the task status until it completes
- Retrieve the generated image URL
Available Image Models
| Model | Description |
|---|---|
wan2.7-image-pro |
Highest quality, supports up to 4096x4096 |
wan2.7-image |
Great quality, supports up to 2048x2048 |
wan2.6-image |
Previous generation, up to 1280x1280 |
qwen-image-plus |
Fixed presets, default 1664x928 |
import requests
import time
API_KEY = os.getenv("DASHSCOPE_API_KEY")
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
"X-DashScope-Async": "enable" # Enable async mode
}
# Step 1: Submit the image generation task
payload = {
"model": "wan2.7-image",
"input": {
"messages": [
{
"role": "user",
"content": [
{
"text": "A serene mountain lake at sunrise, with mist rolling over calm water, "
"pine trees reflected in the surface, photorealistic style"
}
]
}
]
},
"parameters": {
"size": "1024x1024",
"n": 1
}
}
response = requests.post(
"https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/image-generation/generation",
headers=HEADERS,
json=payload
)
result = response.json()
task_id = result["output"]["task_id"]
print(f"Task submitted! Task ID: {task_id}")
print(f"Status: {result['output']['task_status']}")# Step 2: Poll the task until it completes
poll_headers = {
"Authorization": f"Bearer {API_KEY}"
}
while True:
status_response = requests.get(
f"https://dashscope-intl.aliyuncs.com/api/v1/tasks/{task_id}",
headers=poll_headers
)
status_result = status_response.json()
task_status = status_result["output"]["task_status"]
print(f"Status: {task_status}")
if task_status in ["SUCCEEDED", "FAILED"]:
break
time.sleep(3) # Wait 3 seconds before polling again
# Step 3: Get the generated image URL
if task_status == "SUCCEEDED":
image_url = status_result["output"]["results"][0]["url"]
print(f"\nImage generated successfully!")
print(f"URL: {image_url}")
else:
print(f"\nTask failed: {status_result}")# Display the generated image inline (optional)
from IPython.display import Image, display
if task_status == "SUCCEEDED":
display(Image(url=image_url, width=512))
else:
print("No image to display — the task did not succeed.")9. Function Calling (Tool Use)
Function calling enables the model to use external tools — such as APIs, databases, or custom functions — to answer questions it cannot solve on its own.
How it works:
- You send the user question + a list of available tools to the model
- The model decides which tool to call and returns the tool name + parameters
- Your application executes the tool and gets the result
- You send the tool result back to the model
- The model generates a final natural-language response
9.1 Define Your Tools
import json
# Define the tools the model can call
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "Get the current weather for a specific city.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name, e.g. Singapore or New York"
}
},
"required": ["location"]
}
}
}
]
# Simulate the tool (in production, this would call a real weather API)
def get_current_weather(location: str) -> str:
"""Simulated weather lookup."""
weather_data = {
"Singapore": "Partly cloudy, 31C, humidity 78%",
"New York": "Sunny, 22C, humidity 45%",
"London": "Overcast, 15C, humidity 82%",
}
return weather_data.get(location, f"Weather data unavailable for {location}")
print("Tools and simulated function defined.")9.2 Complete Function Calling Workflow
messages = [
{"role": "user", "content": "What is the weather like in Singapore today?"}
]
# Step 1: First model call — the model decides to call a tool
response = client.chat.completions.create(
model="qwen3.6-plus",
messages=messages,
tools=tools
)
assistant_message = response.choices[0].message
print("Step 1 — Model wants to call a tool:")
if assistant_message.tool_calls:
tool_call = assistant_message.tool_calls[0]
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
print(f" Tool: {function_name}")
print(f" Args: {function_args}")
# Step 2: Execute the tool
tool_result = get_current_weather(**function_args)
print(f"\nStep 2 — Tool result: {tool_result}")
# Step 3: Send the tool result back to the model
messages.append(assistant_message.model_dump()) # Add assistant tool call message
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": tool_result
})
# Step 4: Second model call — the model generates the final answer
final_response = client.chat.completions.create(
model="qwen3.6-plus",
messages=messages,
tools=tools
)
print(f"\nStep 3 — Final response:")
print(final_response.choices[0].message.content)
else:
print(" Model responded directly (no tool call needed):")
print(f" {assistant_message.content}")9.3 Parallel Tool Calls
When the user asks about multiple independent things, the model can call several tools at once.
messages = [
{"role": "user", "content": "What is the weather in Singapore and London?"}
]
response = client.chat.completions.create(
model="qwen3.6-plus",
messages=messages,
tools=tools,
parallel_tool_calls=True # Allow multiple tool calls in one response
)
assistant_message = response.choices[0].message
if assistant_message.tool_calls:
print(f"Model requested {len(assistant_message.tool_calls)} parallel tool calls:\n")
messages.append(assistant_message.model_dump())
# Execute all tool calls
for tool_call in assistant_message.tool_calls:
args = json.loads(tool_call.function.arguments)
result = get_current_weather(**args)
print(f" Tool: {tool_call.function.name}({args}) -> {result}")
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result
})
# Get final response
final = client.chat.completions.create(
model="qwen3.6-plus",
messages=messages,
tools=tools
)
print(f"\nFinal answer:\n{final.choices[0].message.content}")9.4 Forced Tool Choice
You can force the model to call (or not call) a specific tool using tool_choice:
# Force a specific tool
tool_choice={"type": "function", "function": {"name": "get_current_weather"}}
# Block all tools (force text-only response)
tool_choice="none"
9.5 Best Practices for Function Calling
- Test tool selection accuracy before going to production
- Keep tool descriptions clear — the model uses them to decide when to call each tool
- Limit the number of tools — smaller candidate sets improve accuracy
- Add human confirmation for write operations (e.g. sending emails, making purchases)
- Set timeouts and fallbacks — tools can fail, so provide graceful error handling
- Note: Tool descriptions count as input tokens and are billed as part of the prompt
10. Structured Output
You can instruct the model to return JSON that conforms to a specific schema. This is useful for extracting structured data from unstructured text.
10.1 JSON Mode (Simple)
completion = client.chat.completions.create(
model="qwen3.6-plus",
messages=[
{
"role": "system",
"content": "You extract contact information from text. Return a JSON object with keys: name, email, phone, company."
},
{
"role": "user",
"content": "Hi, I am Sarah Chen from TechCorp. You can reach me at sarah.chen@techcorp.com or call 555-0142."
}
],
response_format={"type": "json_object"}
)
result = json.loads(completion.choices[0].message.content)
print(json.dumps(result, indent=2))10.2 JSON Schema Mode (Strict)
For tighter control, provide a JSON Schema that the model must conform to:
completion = client.chat.completions.create(
model="qwen3.6-plus",
messages=[
{
"role": "system",
"content": "You are a recipe analyzer. Extract the recipe details from the user text."
},
{
"role": "user",
"content": "To make a classic margherita pizza, you need pizza dough, 200g mozzarella, 150ml tomato sauce, fresh basil leaves, olive oil, and a pinch of salt. Preheat oven to 250C, spread sauce on dough, add cheese, bake for 10-12 minutes, then top with basil."
}
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "recipe",
"schema": {
"type": "object",
"properties": {
"dish_name": {"type": "string"},
"ingredients": {
"type": "array",
"items": {"type": "string"}
},
"steps": {
"type": "array",
"items": {"type": "string"}
},
"cooking_time_minutes": {"type": "integer"},
"temperature_celsius": {"type": "integer"}
},
"required": ["dish_name", "ingredients", "steps", "cooking_time_minutes", "temperature_celsius"]
}
}
}
)
recipe = json.loads(completion.choices[0].message.content)
print(json.dumps(recipe, indent=2))11. Async Requests for High Throughput
For high-concurrency workloads, use AsyncOpenAI to send multiple requests in parallel.
import asyncio
from openai import AsyncOpenAI
async_client = AsyncOpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
)
async def ask_question(question: str) -> str:
"""Send a single async request."""
print(f"Sent: {question}")
response = await async_client.chat.completions.create(
model="qwen3.6-plus",
messages=[{"role": "user", "content": question}]
)
answer = response.choices[0].message.content
print(f"Received answer for: {question[:50]}...")
return answer
async def main():
questions = [
"Summarize the benefits of solar energy in three bullet points.",
"Write a subject line for a product launch email.",
"Translate 'Welcome to our platform' into Spanish."
]
# Send all questions concurrently
results = await asyncio.gather(*[ask_question(q) for q in questions])
print("\n" + "=" * 60)
for q, a in zip(questions, results):
print(f"\nQ: {q}")
print(f"A: {a}")
# In Jupyter notebooks, use await directly
await main()Note: In Jupyter notebooks, you can use
await main()directly since Jupyter already runs an async event loop. In regular Python scripts, useasyncio.run(main())instead.
12. Next Steps & Resources
Congratulations! You have explored the core capabilities of Qwen Cloud. Here is a summary of what we covered and where to go next.
What We Covered
| Section | Capability | Key API |
|---|---|---|
| 2-3 | Text generation | chat.completions.create() |
| 4 | Streaming | stream=True |
| 5 | Thinking / Reasoning | enable_thinking=True |
| 6-7 | Vision (image & video) | image_url / video_url content types |
| 8 | Image generation | DashScope async API |
| 9 | Function calling | tools parameter |
| 10 | Structured output | response_format |
| 11 | Async requests | AsyncOpenAI |
API Endpoints Reference
| API Style | Base URL |
|---|---|
| OpenAI Compatible — Chat Completions | https://dashscope-intl.aliyuncs.com/compatible-mode/v1 |
| OpenAI Compatible — Responses API | https://dashscope-intl.aliyuncs.com/api/v2/apps/protocols/compatible-mode/v1 |
| DashScope — Text Generation | https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/text-generation/generation |
| DashScope — Multimodal Generation | https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation |
| DashScope — Image Generation | https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/image-generation/generation |
Explore More
- Model Gallery — Browse all available models
- Text Generation Guide — Deep dive into text generation
- Vision Guide — Advanced image and video analysis
- Image Generation — Image editing, Wan models
- Video Generation — Text-to-video and image-to-video
- Speech (TTS) — Text-to-speech with voice cloning
- Function Calling — Build agentic apps
- Structured Output — JSON schema enforcement
- Pricing — Pricing details
- Free Quota — Available free credits
- API Reference — Full API documentation
This tutorial was generated based on the Qwen Cloud Developer Guide. Visit the official documentation for the latest updates and additional features.