<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Germey</title>
    <description>The latest articles on DEV Community by Germey (@germey).</description>
    <link>https://dev.to/germey</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834065%2F818e1a39-4e87-4a5b-b466-f76b66eb7301.png</url>
      <title>DEV Community: Germey</title>
      <link>https://dev.to/germey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/germey"/>
    <language>en</language>
    <item>
      <title>A Practical Guide to Music Generation in Claude Code with MCP</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Sat, 22 Aug 2026 01:07:52 +0000</pubDate>
      <link>https://dev.to/germey/a-practical-guide-to-music-generation-in-claude-code-with-mcp-81a</link>
      <guid>https://dev.to/germey/a-practical-guide-to-music-generation-in-claude-code-with-mcp-81a</guid>
      <description></description>
      <category>ai</category>
      <category>devtools</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Build an Async Text-to-Video Workflow with a REST API</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Thu, 20 Aug 2026 01:17:06 +0000</pubDate>
      <link>https://dev.to/germey/how-to-build-an-async-text-to-video-workflow-with-a-rest-api-4f10</link>
      <guid>https://dev.to/germey/how-to-build-an-async-text-to-video-workflow-with-a-rest-api-4f10</guid>
      <description>&lt;p&gt;Video generation is easy to demo and surprisingly easy to make unreliable: a browser request waits too long, the user refreshes, and the app loses track of the generated file.&lt;/p&gt;

&lt;p&gt;This guide shows a practical way to integrate the Hailuo Videos Generation API as an asynchronous workflow. The goal is not just to send a prompt, but to keep enough state to connect a request, a callback, and the final &lt;code&gt;video_url&lt;/code&gt; inside your own product.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do
&lt;/h2&gt;

&lt;p&gt;The documented endpoint is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Base URL: &lt;code&gt;https://api.acedata.cloud&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Endpoint: &lt;code&gt;POST /hailuo/videos&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Headers: &lt;code&gt;accept: application/json&lt;/code&gt;, &lt;code&gt;authorization: Bearer {token}&lt;/code&gt;, and &lt;code&gt;content-type: application/json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Action: &lt;code&gt;generate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Models: &lt;code&gt;minimax-t2v&lt;/code&gt; for text-to-video and &lt;code&gt;minimax-i2v&lt;/code&gt; for image-to-video&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main request fields are &lt;code&gt;action&lt;/code&gt;, &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;prompt&lt;/code&gt;, &lt;code&gt;first_image_url&lt;/code&gt;, &lt;code&gt;callback_url&lt;/code&gt;, and &lt;code&gt;async&lt;/code&gt;. The &lt;code&gt;first_image_url&lt;/code&gt; field is required when using &lt;code&gt;minimax-i2v&lt;/code&gt;, and the documentation notes that Base64 is not supported for that field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with a text-to-video request
&lt;/h2&gt;

&lt;p&gt;For a local test, begin with the same endpoint and a minimal JSON body.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'https://api.acedata.cloud/hailuo/videos'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'authorization: Bearer {token}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "action": "generate",
    "model": "minimax-t2v",
    "prompt": "A quiet city street after rain, reflections on the pavement, slow cinematic camera movement"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful response includes &lt;code&gt;success&lt;/code&gt;, &lt;code&gt;task_id&lt;/code&gt;, &lt;code&gt;trace_id&lt;/code&gt;, and a &lt;code&gt;data&lt;/code&gt; array. Each item can include &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;prompt&lt;/code&gt;, &lt;code&gt;video_url&lt;/code&gt;, and &lt;code&gt;state&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"task_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"baf1034c-684c-46be-ae6d-89ebb89b690d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"trace_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3221eb74-1a25-447a-ba69-7d9b310e306c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0pv8yhe4fdrge0cmckpv23pd2g"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"minimax-t2v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Internal heat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"video_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://platform.cdn.acedata.cloud/.../output.mp4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"succeeded"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For an app, store at least &lt;code&gt;task_id&lt;/code&gt;, &lt;code&gt;trace_id&lt;/code&gt;, &lt;code&gt;prompt&lt;/code&gt;, &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;state&lt;/code&gt;, and &lt;code&gt;video_url&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add image-to-video without changing your pipeline
&lt;/h2&gt;

&lt;p&gt;The image-to-video path uses the same endpoint and action, but switches the model to &lt;code&gt;minimax-i2v&lt;/code&gt; and adds &lt;code&gt;first_image_url&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"generate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"minimax-i2v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"first_image_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/first-frame.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Animate the scene with a slow forward camera move and soft natural motion"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because Base64 is not supported for &lt;code&gt;first_image_url&lt;/code&gt;, your app should upload the first frame to object storage or another public URL before calling the API. Keeping both modes in the same &lt;code&gt;video_jobs&lt;/code&gt; table is usually cleaner than building two systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use callbacks for production-style requests
&lt;/h2&gt;

&lt;p&gt;Video generation may take around 1–2 minutes. Holding a client HTTP request open for that long is fragile, so the API supports asynchronous callbacks through &lt;code&gt;callback_url&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'https://api.acedata.cloud/hailuo/videos'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'authorization: Bearer {token}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "action": "generate",
    "model": "minimax-t2v",
    "prompt": "A product prototype floating above a dark desk, subtle light sweep, cinematic 6 second loop",
    "callback_url": "https://example.com/webhooks/hailuo"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;callback_url&lt;/code&gt;, the API immediately returns a JSON object containing &lt;code&gt;task_id&lt;/code&gt;. Later, your webhook receives a POST JSON payload containing the same &lt;code&gt;task_id&lt;/code&gt;, plus fields such as &lt;code&gt;success&lt;/code&gt;, &lt;code&gt;trace_id&lt;/code&gt;, &lt;code&gt;data[].id&lt;/code&gt;, &lt;code&gt;data[].model&lt;/code&gt;, &lt;code&gt;data[].prompt&lt;/code&gt;, &lt;code&gt;data[].video_url&lt;/code&gt;, and &lt;code&gt;data[].state&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A simple webhook handler can look up the local job by &lt;code&gt;task_id&lt;/code&gt;, store &lt;code&gt;trace_id&lt;/code&gt;, &lt;code&gt;state&lt;/code&gt;, and &lt;code&gt;video_url&lt;/code&gt;, then mark the job as finished when &lt;code&gt;state&lt;/code&gt; is &lt;code&gt;succeeded&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle errors deliberately
&lt;/h2&gt;

&lt;p&gt;The documented error response includes &lt;code&gt;success: false&lt;/code&gt;, an &lt;code&gt;error&lt;/code&gt; object, and &lt;code&gt;trace_id&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api_error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fetch failed"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"trace_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2cf86e86-22a4-46e1-ac2f-032c0f2a4e89"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Common codes include &lt;code&gt;invalid_token&lt;/code&gt;, &lt;code&gt;too_many_requests&lt;/code&gt;, &lt;code&gt;token_mismatched&lt;/code&gt;, &lt;code&gt;api_not_implemented&lt;/code&gt;, and &lt;code&gt;api_error&lt;/code&gt;. Log the full error object with &lt;code&gt;trace_id&lt;/code&gt;, but show a short message in the UI. If you retry, create a new local attempt record so you can tell which &lt;code&gt;task_id&lt;/code&gt; produced which callback.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical integration shape
&lt;/h2&gt;

&lt;p&gt;A minimal database table might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;video_jobs
- id
- task_id
- trace_id
- model
- prompt
- first_image_url
- callback_url
- state
- video_url
- created_at
- finished_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key idea is simple: treat video generation as a job, not a one-off request. Submit the prompt, store the returned &lt;code&gt;task_id&lt;/code&gt;, and let the callback update the final &lt;code&gt;video_url&lt;/code&gt; when it arrives.&lt;/p&gt;

&lt;p&gt;If you want the full field reference and original examples, read the Hailuo Videos Generation API documentation: &lt;a href="https://platform.acedata.cloud/documents/hailuo-videos-generation-integration" rel="noopener noreferrer"&gt;https://platform.acedata.cloud/documents/hailuo-videos-generation-integration&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>A Practical Guide to the Ace Data Cloud Python SDK</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Wed, 19 Aug 2026 01:13:04 +0000</pubDate>
      <link>https://dev.to/germey/a-practical-guide-to-the-ace-data-cloud-python-sdk-4k3l</link>
      <guid>https://dev.to/germey/a-practical-guide-to-the-ace-data-cloud-python-sdk-4k3l</guid>
      <description>&lt;p&gt;If your AI prototype has grown from one &lt;code&gt;curl&lt;/code&gt; command into a small service, the next pain point is usually not the model — it is handling retries, streaming, async calls, task results, and errors without filling your codebase with one-off HTTP wrappers.&lt;/p&gt;

&lt;p&gt;The Ace Data Cloud Python SDK is designed for that middle ground: you still call familiar API shapes such as chat completions and image generation, but you get a Python client with synchronous and asynchronous modes, SSE streaming support, retries, typed exceptions, and plain &lt;code&gt;dict&lt;/code&gt; responses.&lt;/p&gt;

&lt;p&gt;This guide turns the Python SDK documentation into a practical setup you can reuse in a CLI tool, FastAPI backend, automation script, or internal agent service.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;acedatacloud&lt;/code&gt; package wraps services on &lt;code&gt;api.acedata.cloud&lt;/code&gt; into typed methods. The documentation calls out examples such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;client.openai.chat.completions.create(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;client.images.generate(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;client.search.google(...)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Under the hood it is built on &lt;code&gt;httpx&lt;/code&gt;. The SDK supports SSE streaming, automatic retries, typed exceptions, and pydantic-style validation behavior, while response bodies are returned as regular Python &lt;code&gt;dict&lt;/code&gt; objects.&lt;/p&gt;

&lt;p&gt;That last part is worth noticing if you are migrating from &lt;code&gt;openai-python&lt;/code&gt;: the docs explicitly note that the response body uniformly returns &lt;code&gt;dict&lt;/code&gt;, not a pydantic model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install and prepare the token
&lt;/h2&gt;

&lt;p&gt;Install the SDK from PyPI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;acedatacloud
&lt;span class="c"&gt;# or uv add / poetry add&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use the X402 payment path instead of the API token path, the documentation lists a separate package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;acedatacloud-x402
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the normal token-based path, export your token in the shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ACEDATACLOUD_API_TOKEN&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;token&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK reads &lt;code&gt;ACEDATACLOUD_API_TOKEN&lt;/code&gt; automatically when you construct a client without passing &lt;code&gt;api_token&lt;/code&gt;. The docs also mention a common repository convention where teams store &lt;code&gt;ACEDATACLOUD_API_KEY&lt;/code&gt;; in that case, pass it explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;acedatacloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AceDataCloud&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AceDataCloud&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACEDATACLOUD_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Call chat completions synchronously
&lt;/h2&gt;

&lt;p&gt;A simple synchronous request is the best smoke test because it verifies authentication, model routing, and response parsing in one call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;acedatacloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AceDataCloud&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AceDataCloud&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACEDATACLOUD_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;t0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reply with exactly: ADC_PY_SDK_OK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;elapsed_ms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;completion_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;res&lt;/code&gt; is a &lt;code&gt;dict&lt;/code&gt;, you access fields with normal dictionary indexing. The example also prints &lt;code&gt;usage&lt;/code&gt;, which is useful in real services for logging token consumption alongside your own request ID.&lt;/p&gt;

&lt;p&gt;In production, I would wrap this in a small function that accepts &lt;code&gt;messages&lt;/code&gt;, &lt;code&gt;model&lt;/code&gt;, and &lt;code&gt;max_tokens&lt;/code&gt;, then logs &lt;code&gt;res["id"]&lt;/code&gt; plus your user or job identifier. That gives you a clean trail for debugging later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stream responses for UI feedback
&lt;/h2&gt;

&lt;p&gt;When &lt;code&gt;stream=True&lt;/code&gt;, the SDK returns a regular generator. Each yielded item is a parsed chunk dictionary following the OpenAI SSE format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;acedatacloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AceDataCloud&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AceDataCloud&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACEDATACLOUD_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;t0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;first_chunk_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;collected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Count from 1 to 5, separated by single spaces, no extra text.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;first_chunk_ms&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;first_chunk_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;[{}])[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;delta&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;collected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;first_chunk_ms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_chunk_ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chunks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;collected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collected&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern maps nicely to web apps: read each &lt;code&gt;delta&lt;/code&gt;, forward it to your frontend SSE endpoint, and keep the final joined text for persistence or audit logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the async client in services
&lt;/h2&gt;

&lt;p&gt;For FastAPI, aiohttp, or any asyncio service, use &lt;code&gt;AsyncAceDataCloud&lt;/code&gt;. The API is symmetrical, but I/O methods return coroutines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;acedatacloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncAceDataCloud&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AsyncAceDataCloud&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACEDATACLOUD_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;t0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reply with exactly: ADC_PY_ASYNC_OK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;elapsed_ms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The docs recommend explicitly closing the async client to shut down the connection pool. In a long-running service, create the client once during startup and close it once during shutdown rather than creating a new client per request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate an image with &lt;code&gt;images.generate&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The Python SDK also exposes image generation. The documentation includes a NanoBanana example and notes that NanoBanana is synchronous, so you should not pass &lt;code&gt;wait&lt;/code&gt;; the call blocks until the upstream returns &lt;code&gt;200&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;acedatacloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AceDataCloud&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AceDataCloud&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACEDATACLOUD_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;t0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nano-banana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nano-banana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A minimalist logo of a yellow banana on a white background, flat design&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;elapsed_ms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trace_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trace_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For longer-running asynchronous services such as Midjourney, Sora, Veo, and Suno, the docs point to task polling with &lt;code&gt;wait=True&lt;/code&gt; or &lt;code&gt;TaskHandle.wait()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle errors deliberately
&lt;/h2&gt;

&lt;p&gt;The SDK includes typed exceptions such as &lt;code&gt;AuthenticationError&lt;/code&gt;, &lt;code&gt;RateLimitError&lt;/code&gt;, and &lt;code&gt;ValidationError&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;acedatacloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AceDataCloud&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;acedatacloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AuthenticationError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RateLimitError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt;

&lt;span class="n"&gt;bad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AceDataCloud&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;definitely-not-a-real-token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;bad&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;AuthenticationError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;err_class&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;RateLimitError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rate limited:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bad request:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A useful production rule is to treat these differently: authentication errors should alert configuration owners, rate limits should trigger backoff or queueing, and validation errors should usually be fixed before retrying.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure timeouts, retries, and base URLs
&lt;/h2&gt;

&lt;p&gt;The documented client options include &lt;code&gt;api_token&lt;/code&gt;, &lt;code&gt;base_url&lt;/code&gt;, &lt;code&gt;platform_base_url&lt;/code&gt;, &lt;code&gt;timeout&lt;/code&gt;, &lt;code&gt;max_retries&lt;/code&gt;, and custom &lt;code&gt;headers&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;acedatacloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AceDataCloud&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AceDataCloud&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.acedata.cloud&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;platform_base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://platform.acedata.cloud&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;300.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_retries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-service/1.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One migration footnote from the docs: Python SDK timeout values and task polling values are in seconds, while the TypeScript SDK uses milliseconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple service shape
&lt;/h2&gt;

&lt;p&gt;For most projects, I would start with three files:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;settings.py&lt;/code&gt; reads &lt;code&gt;ACEDATACLOUD_API_TOKEN&lt;/code&gt; or your explicit secret name.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ai_client.py&lt;/code&gt; creates one &lt;code&gt;AceDataCloud&lt;/code&gt; or &lt;code&gt;AsyncAceDataCloud&lt;/code&gt; instance.&lt;/li&gt;
&lt;li&gt;Feature code calls small wrapper functions for chat, streaming, or image generation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That keeps the SDK boundary obvious and makes it easy to swap sync for async later.&lt;/p&gt;

&lt;p&gt;Read the full Python SDK documentation here: &lt;a href="https://platform.acedata.cloud/documents/sdk-python" rel="noopener noreferrer"&gt;https://platform.acedata.cloud/documents/sdk-python&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to Add Remote MCP Tools to Claude Code Without Leaving Your Terminal</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Mon, 17 Aug 2026 01:07:32 +0000</pubDate>
      <link>https://dev.to/germey/how-to-add-remote-mcp-tools-to-claude-code-without-leaving-your-terminal-4hdf</link>
      <guid>https://dev.to/germey/how-to-add-remote-mcp-tools-to-claude-code-without-leaving-your-terminal-4hdf</guid>
      <description>&lt;p&gt;When you are already using Claude Code for day-to-day development, the next bottleneck is often not code generation itself, but all the side tasks around it: searching, preparing visuals, creating demo assets, or shortening links without breaking your flow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fseewxl2lsn9143h5jhes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fseewxl2lsn9143h5jhes.png" alt="Claude Code MCP workflow cover" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This guide walks through a practical setup: adding remote MCP servers to Claude Code so the assistant can call external tools from the same terminal session where you write and review code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do
&lt;/h2&gt;

&lt;p&gt;Claude Code is Anthropic's command-line programming assistant. With MCP servers attached, it can still help with coding and refactoring, but it can also reach out to tool-specific services during a task.&lt;/p&gt;

&lt;p&gt;The source documentation lists several managed remote MCP servers, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;https://suno.mcp.acedata.cloud/mcp&lt;/code&gt; for music workflows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://midjourney.mcp.acedata.cloud/mcp&lt;/code&gt; for image generation and editing workflows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://flux.mcp.acedata.cloud/mcp&lt;/code&gt; for image workflows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://seedream.mcp.acedata.cloud/mcp&lt;/code&gt; for image workflows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://nanobanana.mcp.acedata.cloud/mcp&lt;/code&gt; for Gemini-driven image editing workflows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://luma.mcp.acedata.cloud/mcp&lt;/code&gt; for video workflows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://veo.mcp.acedata.cloud/mcp&lt;/code&gt; for video workflows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://seedance.mcp.acedata.cloud/mcp&lt;/code&gt; for video workflows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://serp.mcp.acedata.cloud/mcp&lt;/code&gt; for search workflows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://shorturl.mcp.acedata.cloud/mcp&lt;/code&gt; for shortening links&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The useful part is not that these exist as separate products. The useful part is that you can wire only the servers you need into Claude Code and then ask for a complete workflow in plain language.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Claude Code can register a remote MCP server with &lt;code&gt;claude mcp add&lt;/code&gt;. The configuration uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a server name, such as &lt;code&gt;serp&lt;/code&gt; or &lt;code&gt;shorturl&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--transport http&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;the remote MCP URL&lt;/li&gt;
&lt;li&gt;an HTTP authorization header: &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A minimal command looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add serp &lt;span class="nt"&gt;--transport&lt;/span&gt; http https://serp.mcp.acedata.cloud/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details are worth calling out.&lt;/p&gt;

&lt;p&gt;First, &lt;code&gt;-H&lt;/code&gt; must be uppercase because lowercase &lt;code&gt;-h&lt;/code&gt; means help. Second, if you do not pass a scope, Claude Code uses the default local scope, which only applies in the current directory. If you want the same server available across projects, add &lt;code&gt;-s user&lt;/code&gt;. If you want a project-level setup, use a project configuration file instead.&lt;/p&gt;

&lt;p&gt;After adding one or more servers, check what Claude Code sees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the servers you registered as HTTP MCP servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1: add only the tools you need
&lt;/h2&gt;

&lt;p&gt;For most developers, I would start with a small set rather than registering every server at once. For example, if you are writing technical posts from your terminal, search and short links are enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add serp &lt;span class="nt"&gt;--transport&lt;/span&gt; http https://serp.mcp.acedata.cloud/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_TOKEN"&lt;/span&gt;

claude mcp add shorturl &lt;span class="nt"&gt;--transport&lt;/span&gt; http https://shorturl.mcp.acedata.cloud/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, inside Claude Code, you can ask for a workflow like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Search for recent AI video generation trends, summarize the useful references, draft a technical outline, and shorten the key links.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a good fit for MCP because the assistant can combine reasoning, web search, and link handling without you copying data between browser tabs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2: keep project MCP config in &lt;code&gt;.mcp.json&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If the workflow belongs to a repository, you can define MCP servers in a &lt;code&gt;.mcp.json&lt;/code&gt; file at the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"serp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://serp.mcp.acedata.cloud/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer YOUR_TOKEN"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"shorturl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://shorturl.mcp.acedata.cloud/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer YOUR_TOKEN"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project-level file is useful when a team wants the same MCP names and URLs. Do not commit real tokens to a public repository. In practice, keep &lt;code&gt;.mcp.json&lt;/code&gt; out of Git or replace secrets with environment-specific values that each developer fills in locally.&lt;/p&gt;

&lt;p&gt;The documentation also notes that project-level &lt;code&gt;.mcp.json&lt;/code&gt; takes precedence over the global Claude configuration. That makes it a reasonable place to encode repository-specific tool choices while still allowing personal global defaults elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  A realistic builder workflow
&lt;/h2&gt;

&lt;p&gt;Here is a workflow I would actually use while maintaining a developer-facing project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask Claude Code to inspect a README or docs folder.&lt;/li&gt;
&lt;li&gt;Use search via the &lt;code&gt;serp&lt;/code&gt; MCP server to find current references for a section that depends on recent ecosystem behavior.&lt;/li&gt;
&lt;li&gt;Ask Claude Code to rewrite the section with citations or summarized links.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;shorturl&lt;/code&gt; only for links that will be pasted into release notes or social posts.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;claude mcp list&lt;/code&gt; if a tool is not being called as expected.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For more asset-heavy work, you could add image, music, or video MCP servers later, but the operational pattern stays the same: register the HTTP endpoint, pass the &lt;code&gt;Authorization&lt;/code&gt; header, and let Claude Code call the tool when the task requires it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting checklist
&lt;/h2&gt;

&lt;p&gt;If a server is not working, start with the boring checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;claude mcp list&lt;/code&gt; and confirm the server is present.&lt;/li&gt;
&lt;li&gt;Remove and re-add the server if the URL or header is wrong.&lt;/li&gt;
&lt;li&gt;Confirm you used uppercase &lt;code&gt;-H&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Check whether the server was added locally, globally with &lt;code&gt;-s user&lt;/code&gt;, or through project &lt;code&gt;.mcp.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Make sure you did not paste a placeholder token by mistake.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The nice thing about this setup is that it is reversible. You can start with one MCP server, validate the workflow, and add more only when they remove real friction.&lt;/p&gt;

&lt;p&gt;If you want the complete list of remote MCP URLs and the broader Claude Code examples, the original Ace Data Cloud guide is here: &lt;a href="https://platform.acedata.cloud/documents/claude-code-mcp-all" rel="noopener noreferrer"&gt;https://platform.acedata.cloud/documents/claude-code-mcp-all&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Use an OpenAI-Compatible Endpoint in WorkBuddy</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Sun, 16 Aug 2026 01:05:17 +0000</pubDate>
      <link>https://dev.to/germey/how-to-use-an-openai-compatible-endpoint-in-workbuddy-j92</link>
      <guid>https://dev.to/germey/how-to-use-an-openai-compatible-endpoint-in-workbuddy-j92</guid>
      <description>&lt;p&gt;If you use a desktop AI workspace for real coding or office automation, the annoying part is rarely the chat UI. It is model routing: one task wants a long-context model, another wants a reasoning model, and another just needs a fast OpenAI-compatible chat endpoint.&lt;/p&gt;

&lt;p&gt;This guide walks through a practical setup for using Ace Data Cloud as an OpenAI-compatible provider inside WorkBuddy, the desktop AI workspace from the Tencent CodeBuddy family. The goal is not to replace WorkBuddy's built-in models. It is to add your own custom model entries so you can switch between model families from the same WorkBuddy model picker.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do
&lt;/h2&gt;

&lt;p&gt;WorkBuddy supports custom models through an "OpenAI compatible protocol API" flow. Ace Data Cloud exposes chat-completion endpoints that match that shape, so the setup is mostly a matter of entering the right interface address, API key, and model name.&lt;/p&gt;

&lt;p&gt;The document lists these interface addresses:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model family&lt;/th&gt;
&lt;th&gt;Interface address&lt;/th&gt;
&lt;th&gt;Example model IDs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude / general entry&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://api.acedata.cloud/v1/chat/completions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;claude-opus-4-8&lt;/code&gt;, &lt;code&gt;claude-sonnet-4-6&lt;/code&gt;, &lt;code&gt;claude-haiku-4-5-20251001&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://api.acedata.cloud/openai/chat/completions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gpt-5.6-sol&lt;/code&gt;, &lt;code&gt;gpt-5.6-luna&lt;/code&gt;, &lt;code&gt;gpt-5.5&lt;/code&gt;, &lt;code&gt;gpt-5.5-pro&lt;/code&gt;, &lt;code&gt;gpt-5.2&lt;/code&gt;, &lt;code&gt;gpt-4o&lt;/code&gt;, &lt;code&gt;o3&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://api.acedata.cloud/gemini/chat/completions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gemini-3.1-pro&lt;/code&gt;, &lt;code&gt;gemini-3.0-pro&lt;/code&gt;, &lt;code&gt;gemini-3.5-flash&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grok&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://api.acedata.cloud/grok/chat/completions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;grok-4&lt;/code&gt;, &lt;code&gt;grok-3&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kimi&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://api.acedata.cloud/kimi/chat/completions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;kimi-k3&lt;/code&gt;, &lt;code&gt;kimi-k2.6&lt;/code&gt;, &lt;code&gt;kimi-k2.5&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://api.acedata.cloud/glm/chat/completions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;glm-5.1&lt;/code&gt;, &lt;code&gt;glm-4.7&lt;/code&gt;, &lt;code&gt;glm-4.6&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://api.acedata.cloud/deepseek/chat/completions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;deepseek-r1&lt;/code&gt;, &lt;code&gt;deepseek-v3&lt;/code&gt;, &lt;code&gt;deepseek-v4-flash&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The most convenient path is the general entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.acedata.cloud/v1/chat/completions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to the guide, &lt;code&gt;/v1/chat/completions&lt;/code&gt; can route to different model IDs, not only Claude. If you prefer separate entries per model family, use one of the family-specific addresses above.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works in WorkBuddy
&lt;/h2&gt;

&lt;p&gt;Open WorkBuddy and go to &lt;strong&gt;Settings → Models&lt;/strong&gt;. In the &lt;strong&gt;Custom Models&lt;/strong&gt; area, click &lt;strong&gt;Add Model&lt;/strong&gt;. The add-model dialog is where the important mapping happens:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;WorkBuddy field&lt;/th&gt;
&lt;th&gt;Value to enter&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Provider&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Custom&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface Address&lt;/td&gt;
&lt;td&gt;For example, &lt;code&gt;https://api.acedata.cloud/v1/chat/completions&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API KEY&lt;/td&gt;
&lt;td&gt;Your Ace Data Cloud API Token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model Name&lt;/td&gt;
&lt;td&gt;A real model ID, such as &lt;code&gt;claude-opus-4-8&lt;/code&gt;, &lt;code&gt;gpt-5.5&lt;/code&gt;, or &lt;code&gt;gemini-3.1-pro&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The interface address should include the full &lt;code&gt;/chat/completions&lt;/code&gt; path. The guide notes that WorkBuddy validates the path according to OpenAI standards when &lt;strong&gt;Custom Protocol&lt;/strong&gt; is off, and Ace Data Cloud's entry is already the complete standard path.&lt;/p&gt;

&lt;p&gt;A minimal OpenAI-style request against the general endpoint looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.acedata.cloud/v1/chat/completions"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_API_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "claude-opus-4-8",
    "messages": [
      {
        "role": "user",
        "content": "Review this function and suggest a safer implementation."
      }
    ]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact request is useful for sanity-checking the same endpoint and model name you put into WorkBuddy. If the model name is wrong, the document says the Ace Data Cloud gateway will reject the request and WorkBuddy will surface a 4xx error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the advanced switches
&lt;/h2&gt;

&lt;p&gt;WorkBuddy exposes several capability switches for custom models. These are not decoration; they tell the client how to treat the model in the UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool Calling&lt;/strong&gt; declares that the model supports function calling or tools. The guide says mainstream Ace Data Cloud models such as Claude, GPT, Gemini, Grok, Kimi, and DeepSeek support this, so it can be enabled when your chosen model supports tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Image Input&lt;/strong&gt; is for multimodal image input. Enable it only for model IDs that support multimodal input, such as the examples named in the document: &lt;code&gt;claude-opus-4-8&lt;/code&gt;, &lt;code&gt;gpt-5.5&lt;/code&gt;, or &lt;code&gt;gemini-3.1-pro&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inference Mode&lt;/strong&gt; is for models with a visible reasoning or thinking process. The guide names &lt;code&gt;o3&lt;/code&gt;, &lt;code&gt;deepseek-r1&lt;/code&gt;, &lt;code&gt;gemini-3.1-pro&lt;/code&gt;, and &lt;code&gt;grok-4&lt;/code&gt; as examples where this may apply.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom Protocol&lt;/strong&gt; should stay off for this setup. With it off, WorkBuddy validates and uses the standard &lt;code&gt;/chat/completions&lt;/code&gt; behavior; Ace Data Cloud's endpoint already matches that expectation.&lt;/p&gt;

&lt;p&gt;For context-window dropdowns, the document recommends leaving both &lt;strong&gt;Input&lt;/strong&gt; and &lt;strong&gt;Output&lt;/strong&gt; as &lt;strong&gt;Use Provider Default&lt;/strong&gt;, so WorkBuddy uses the upstream model's window configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding more than one model
&lt;/h2&gt;

&lt;p&gt;A useful pattern is to add multiple WorkBuddy custom model entries that share the same API key but use different model IDs.&lt;/p&gt;

&lt;p&gt;For example, you might create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Provider: Custom
Interface Address: https://api.acedata.cloud/v1/chat/completions
API KEY: YOUR_API_TOKEN
Model Name: claude-opus-4-8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then repeat the same flow for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Provider: Custom
Interface Address: https://api.acedata.cloud/openai/chat/completions
API KEY: YOUR_API_TOKEN
Model Name: gpt-5.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After saving, both models appear under WorkBuddy's &lt;strong&gt;Custom Models&lt;/strong&gt; group. That makes the model decision a per-task choice instead of a one-time workspace decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting checklist
&lt;/h2&gt;

&lt;p&gt;If WorkBuddy returns an error, check these items first:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The interface address includes &lt;code&gt;/chat/completions&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Protocol&lt;/strong&gt; is off.&lt;/li&gt;
&lt;li&gt;The model name exactly matches a model ID supported by the selected endpoint.&lt;/li&gt;
&lt;li&gt;The API key is the Ace Data Cloud API Token from your console.&lt;/li&gt;
&lt;li&gt;Capability switches match the selected model; for example, do not enable image input for a text-only model.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;WorkBuddy's own documentation says custom model configuration, including the API key, is stored locally in &lt;code&gt;workbuddy/models.json&lt;/code&gt; and is not uploaded to the cloud. WorkBuddy acts as the communication link that forwards your input to the configured API address.&lt;/p&gt;

&lt;p&gt;If you want the source configuration table and screenshots, the full Ace Data Cloud WorkBuddy setup guide is here: &lt;a href="https://platform.acedata.cloud/documents/development_workbuddy" rel="noopener noreferrer"&gt;https://platform.acedata.cloud/documents/development_workbuddy&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>tutorial</category>
      <category>devtools</category>
    </item>
    <item>
      <title>A Practical Guide to Running OpenCode with an OpenAI-Compatible Provider</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Sat, 15 Aug 2026 01:10:09 +0000</pubDate>
      <link>https://dev.to/germey/how-to-run-claude-code-from-your-terminal-with-an-api-proxy-2of2</link>
      <guid>https://dev.to/germey/how-to-run-claude-code-from-your-terminal-with-an-api-proxy-2of2</guid>
      <description>&lt;p&gt;If you use a terminal coding agent every day, one practical problem shows up quickly: how do you keep model access reproducible, project-friendly, and easy to verify without changing the way you work?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjocjop2lyqacqtxfozme.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjocjop2lyqacqtxfozme.png" alt="OpenCode + Ace Data Cloud cover" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OpenCode is a terminal-first coding agent from the SST team. It can read code, modify files, run commands, explain errors, and help with day-to-day development tasks from your shell. The useful part for builders is that OpenCode supports custom model providers, so you can point it at an OpenAI-compatible endpoint while keeping the normal &lt;code&gt;opencode&lt;/code&gt; command and TUI workflow.&lt;/p&gt;

&lt;p&gt;This guide walks through configuring OpenCode to use Ace Data Cloud as a custom provider through its OpenAI Chat Completions-compatible proxy.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do
&lt;/h2&gt;

&lt;p&gt;After the setup, your workflow stays familiar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run &lt;code&gt;opencode&lt;/code&gt; inside a project and choose a model from &lt;code&gt;/models&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;run one-off tasks with &lt;code&gt;opencode run&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;keep your API token out of the JSON config by using an environment variable&lt;/li&gt;
&lt;li&gt;expose only the model IDs you want OpenCode to use&lt;/li&gt;
&lt;li&gt;use either a global config or a project-level config depending on the repository&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key values from the source guide are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Base URL: &lt;code&gt;https://api.acedata.cloud/v1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Chat Completions endpoint used under the hood: &lt;code&gt;https://api.acedata.cloud/v1/chat/completions&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Provider key used in the example: &lt;code&gt;provider.acedatacloud&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;API token environment variable: &lt;code&gt;ACEDATACLOUD_API_KEY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Authorization format: &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Model name format in OpenCode: &lt;code&gt;acedatacloud/&amp;lt;model&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;OpenCode reads &lt;code&gt;opencode.json&lt;/code&gt; at startup. The documented load order is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;global config: &lt;code&gt;~/.config/opencode/opencode.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;a custom config file pointed to by &lt;code&gt;OPENCODE_CONFIG&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;project config: &lt;code&gt;opencode.json&lt;/code&gt; in the project root, up to the Git root&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Later config overrides earlier config. That makes it reasonable to keep a safe global provider definition and still customize models for a specific repository.&lt;/p&gt;

&lt;p&gt;For this integration, OpenCode uses the Vercel AI SDK adapter &lt;code&gt;@ai-sdk/openai-compatible&lt;/code&gt;. When you select a model such as &lt;code&gt;acedatacloud/gpt-5-mini&lt;/code&gt;, OpenCode resolves the &lt;code&gt;acedatacloud&lt;/code&gt; provider block, reads &lt;code&gt;options.baseURL&lt;/code&gt;, resolves &lt;code&gt;{env:ACEDATACLOUD_API_KEY}&lt;/code&gt;, and sends an OpenAI Chat Completions-format request to &lt;code&gt;/v1/chat/completions&lt;/code&gt; with a bearer token.&lt;/p&gt;

&lt;p&gt;No local proxy process is required. No wrapper CLI is required. You still run OpenCode normally; the configuration changes only the underlying API target and model list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install OpenCode
&lt;/h2&gt;

&lt;p&gt;OpenCode supports macOS, Linux, Windows, and WSL. On macOS, Linux, or WSL, the guide lists this one-line installer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://opencode.ai/install | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you prefer package managers, the documented alternatives are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;sst/tap/opencode
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; opencode-ai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows, you can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;winget&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sst.opencode&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installing, reopen your terminal and verify the command is available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opencode &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see &lt;code&gt;command not found&lt;/code&gt;, the current shell probably has not loaded the new PATH yet. Close and reopen the terminal. On macOS with Homebrew, you can also check the binary path with &lt;code&gt;which opencode&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the token out of your config file
&lt;/h2&gt;

&lt;p&gt;First, put your Ace Data Cloud API token into an environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ACEDATACLOUD_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{token}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;{token}&lt;/code&gt; with the token from your console. Put the line in &lt;code&gt;~/.zshrc&lt;/code&gt;, &lt;code&gt;~/.bashrc&lt;/code&gt;, or &lt;code&gt;~/.bash_profile&lt;/code&gt;, then reload it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A subtle issue: if your token lives in a &lt;code&gt;.env&lt;/code&gt; file as &lt;code&gt;ACEDATACLOUD_API_KEY=...&lt;/code&gt; without &lt;code&gt;export&lt;/code&gt;, a plain &lt;code&gt;source .env&lt;/code&gt; only creates a shell variable. Child processes such as OpenCode may not see it. Use this instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; .env &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt; +a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is for OpenCode to resolve &lt;code&gt;{env:ACEDATACLOUD_API_KEY}&lt;/code&gt; when it starts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Register Ace Data Cloud as a provider
&lt;/h2&gt;

&lt;p&gt;Create the global config file if it does not exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.config/opencode
&lt;span class="nb"&gt;touch&lt;/span&gt; ~/.config/opencode/opencode.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add a provider block like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://opencode.ai/config.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"acedatacloud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"npm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@ai-sdk/openai-compatible"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ace Data Cloud"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"baseURL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.acedata.cloud/v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"apiKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{env:ACEDATACLOUD_API_KEY}"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"models"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"claude-sonnet-4-6"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Claude Sonnet 4.6"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"claude-haiku-4-5-20251001"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Claude Haiku 4.5"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"claude-opus-4-7"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Claude Opus 4.7"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"gpt-5"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GPT-5"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"gpt-5-mini"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GPT-5 mini"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"gemini-2.5-pro"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Gemini 2.5 Pro"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"deepseek-v3.2-exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DeepSeek V3.2 Exp"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important pieces are &lt;code&gt;provider.acedatacloud&lt;/code&gt; as the local provider ID, &lt;code&gt;@ai-sdk/openai-compatible&lt;/code&gt; as the adapter, &lt;code&gt;https://api.acedata.cloud/v1&lt;/code&gt; as the base URL, and &lt;code&gt;{env:ACEDATACLOUD_API_KEY}&lt;/code&gt; as the token placeholder. The &lt;code&gt;models&lt;/code&gt; object is the list OpenCode exposes under the provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify and run a smoke test
&lt;/h2&gt;

&lt;p&gt;List the registered models:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opencode models acedatacloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the example config, the output should include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;acedatacloud/claude-haiku-4-5-20251001
acedatacloud/claude-opus-4-7
acedatacloud/claude-sonnet-4-6
acedatacloud/deepseek-v3.2-exp
acedatacloud/gemini-2.5-pro
acedatacloud/gpt-5
acedatacloud/gpt-5-mini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run a simple one-off task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opencode run &lt;span class="nt"&gt;--model&lt;/span&gt; acedatacloud/claude-sonnet-4-6 &lt;span class="s2"&gt;"Reply: Hello from AceData via OpenCode."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The documented test output is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; build · claude-sonnet-4-6

Hello from AceData via OpenCode.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For interactive work, move into a project and start the TUI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/your/project
opencode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use &lt;code&gt;/models&lt;/code&gt; to pick a configured model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting notes
&lt;/h2&gt;

&lt;p&gt;If &lt;code&gt;opencode models acedatacloud&lt;/code&gt; does not show your provider, OpenCode probably did not read the config file. Restart the TUI after edits, or run with &lt;code&gt;--print-logs --log-level INFO&lt;/code&gt; and check which config path is loaded.&lt;/p&gt;

&lt;p&gt;If you get &lt;code&gt;401 Unauthorized&lt;/code&gt;, check that &lt;code&gt;ACEDATACLOUD_API_KEY&lt;/code&gt; is exported in the current shell. If &lt;code&gt;opencode debug config&lt;/code&gt; shows &lt;code&gt;"Authorization": "Bearer "&lt;/code&gt;, the placeholder resolved to an empty value.&lt;/p&gt;

&lt;p&gt;If OpenCode reports &lt;code&gt;Model not found: acedatacloud/...&lt;/code&gt;, make sure that model ID is present as a key under &lt;code&gt;provider.acedatacloud.models&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For MCP-heavy sessions, the guide notes that OpenAI-series models such as &lt;code&gt;gpt-5&lt;/code&gt; or &lt;code&gt;gpt-5-mini&lt;/code&gt; are a good first choice when many MCP tools are mounted, while Claude models work normally for plain conversation.&lt;/p&gt;

&lt;p&gt;If you want the full reference, read the original &lt;a href="https://platform.acedata.cloud/documents/opencode-terminal-integration" rel="noopener noreferrer"&gt;OpenCode Terminal Setup Guide&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devtools</category>
      <category>api</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Use Claude Code in VS Code with a Project-Local API Configuration</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Fri, 14 Aug 2026 01:03:41 +0000</pubDate>
      <link>https://dev.to/germey/how-to-use-claude-code-in-vs-code-with-a-project-local-api-configuration-25mh</link>
      <guid>https://dev.to/germey/how-to-use-claude-code-in-vs-code-with-a-project-local-api-configuration-25mh</guid>
      <description>&lt;p&gt;You want Claude Code inside VS Code, but you also want the configuration to be explicit, project-scoped, and safe to keep out of source control.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do
&lt;/h2&gt;

&lt;p&gt;Claude Code is a programming agent from Anthropic. In VS Code, the Claude Code extension gives you a native editor experience: a sidebar entry, a panel for asking coding questions, and the same underlying settings model used by the terminal CLI.&lt;/p&gt;

&lt;p&gt;The useful part for a team project is that you do not have to put credentials into a shared repository-level config. You can create a local file under the project root, configure the API endpoint there, restart VS Code, and keep the token out of Git.&lt;/p&gt;

&lt;p&gt;The Ace Data Cloud setup described here uses the Claude Code extension with these documented values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;project-local settings file: &lt;code&gt;.claude/settings.local.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;base URL: &lt;code&gt;https://api.acedata.cloud&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;auth token variable: &lt;code&gt;ANTHROPIC_AUTH_TOKEN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;automatic compaction window variable: &lt;code&gt;CLAUDE_CODE_AUTO_COMPACT_WINDOW&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;recommended compaction value from the guide: &lt;code&gt;850000&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is enough to make VS Code launch Claude Code with the project-specific environment you expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The VS Code extension and the terminal CLI read the same Claude Code settings. Instead of signing in through the official Anthropic account flow in the extension, you point Claude Code at the Ace Data Cloud proxy API by setting environment variables in Claude Code's settings file.&lt;/p&gt;

&lt;p&gt;For a single project, create this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/settings.local.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then put the following JSON inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ANTHROPIC_AUTH_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{token}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ANTHROPIC_BASE_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.acedata.cloud"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"CLAUDE_CODE_AUTO_COMPACT_WINDOW"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"850000"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;{token}&lt;/code&gt; with the API token you copied from the Ace Data Cloud console. Keep the file local to the project and avoid committing it. The guide specifically recommends using &lt;code&gt;settings.local.json&lt;/code&gt; for the current project and adding that path to &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;CLAUDE_CODE_AUTO_COMPACT_WINDOW&lt;/code&gt; setting does not change the model's context limit. It sets the trigger window for automatic compression to around 850,000 tokens, leaving room for tool results and final answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Install the Claude Code extension
&lt;/h2&gt;

&lt;p&gt;Open the VS Code extension marketplace and search for &lt;code&gt;Claude Code&lt;/code&gt;. Install the Claude Code extension, then accept the trust prompt shown by VS Code.&lt;/p&gt;

&lt;p&gt;After installation, you should see a Claude Code entry in the VS Code sidebar. Opening it may lead you toward an official login prompt. For this workflow, you can skip that and configure the proxy API through the local settings file instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Add project-local settings
&lt;/h2&gt;

&lt;p&gt;From your project root, create the &lt;code&gt;.claude&lt;/code&gt; directory and the local settings file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; .claude
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .claude/settings.local.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="sh"&gt;'
{
  "env": {
    "ANTHROPIC_AUTH_TOKEN": "{token}",
    "ANTHROPIC_BASE_URL": "https://api.acedata.cloud",
    "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "850000"
  }
}
&lt;/span&gt;&lt;span class="no"&gt;JSON
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now edit the file and replace &lt;code&gt;{token}&lt;/code&gt; with your real API token.&lt;/p&gt;

&lt;p&gt;This pattern is intentionally boring: the base URL is visible, the credential is isolated, and the config is easy to remove when you no longer need it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Keep credentials out of Git
&lt;/h2&gt;

&lt;p&gt;Add the local settings file to &lt;code&gt;.gitignore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\n.claude/settings.local.json\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not put the real token into &lt;code&gt;.claude/settings.json&lt;/code&gt; if that file is shared with the team. The documented project-local file is a better place for personal or machine-specific credentials.&lt;/p&gt;

&lt;p&gt;One more gotcha from the guide: if the environment that launches VS Code already has an old &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt;, unset it first. Do not treat an empty string as cleanup; actually remove the variable from that environment.&lt;/p&gt;

&lt;p&gt;For example, if you launch VS Code from a shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;unset &lt;/span&gt;ANTHROPIC_API_KEY
code &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Restart or reload VS Code
&lt;/h2&gt;

&lt;p&gt;After changing the settings file, restart or reload VS Code so the Claude Code extension reads the new environment values.&lt;/p&gt;

&lt;p&gt;Open the Claude Code panel and ask a small project-aware question, such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read the package scripts and explain how to run the test suite.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a good first prompt because it checks whether the extension can see your workspace and respond in context without making any destructive edits.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use project-local config vs global config
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;.claude/settings.local.json&lt;/code&gt; when you want one repository to use this setup and you do not want to affect every other project on your machine.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;~/.claude/settings.json&lt;/code&gt; only when you deliberately want the same configuration to apply across all projects. Even then, be careful with credential storage and with any older environment variables that might override what you expect.&lt;/p&gt;

&lt;p&gt;For me, the local file is the safer default: it is visible while you are working on the project, easy to audit, and easy to keep out of commits.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small checklist before you start editing code
&lt;/h2&gt;

&lt;p&gt;Before asking Claude Code to modify files, I would check four things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;.claude/settings.local.json&lt;/code&gt; exists in the project root.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ANTHROPIC_BASE_URL&lt;/code&gt; is exactly &lt;code&gt;https://api.acedata.cloud&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ANTHROPIC_AUTH_TOKEN&lt;/code&gt; is set to your token, not a placeholder.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.claude/settings.local.json&lt;/code&gt; is ignored by Git.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once those are true, you can use the Claude Code panel in VS Code as your normal coding assistant: ask it to explain code, inspect errors, propose diffs, or help navigate an unfamiliar repository.&lt;/p&gt;

&lt;p&gt;The original Ace Data Cloud guide is here if you want to compare the exact setup notes: &lt;a href="https://platform.acedata.cloud/documents/claude-code-vscode-integrations" rel="noopener noreferrer"&gt;Claude Code VS Code setup guide&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>devtools</category>
    </item>
    <item>
      <title>How to Run Claude Code from Your Terminal with an API Proxy</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Thu, 13 Aug 2026 01:07:38 +0000</pubDate>
      <link>https://dev.to/germey/how-to-run-claude-code-from-your-terminal-with-an-api-proxy-249b</link>
      <guid>https://dev.to/germey/how-to-run-claude-code-from-your-terminal-with-an-api-proxy-249b</guid>
      <description>&lt;p&gt;You want Claude Code to behave like a normal local CLI tool, but you also want the API connection to be explicit, reproducible, and easy to verify across projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do
&lt;/h2&gt;

&lt;p&gt;Claude Code is a terminal-based coding agent. Once it is installed, you can run &lt;code&gt;claude&lt;/code&gt; inside a project directory and ask it to read code, modify files, run commands, explain errors, or inspect a recent diff.&lt;/p&gt;

&lt;p&gt;The useful part for builders is that Claude Code already reads standard environment variables before it sends requests. That means you can keep the native &lt;code&gt;claude&lt;/code&gt; workflow while pointing the underlying API traffic at Ace Data Cloud:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Base URL: &lt;code&gt;https://api.acedata.cloud&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Auth variable: &lt;code&gt;ANTHROPIC_AUTH_TOKEN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Optional local project config: &lt;code&gt;.claude/settings.local.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Optional context compaction trigger: &lt;code&gt;CLAUDE_CODE_AUTO_COMPACT_WINDOW="850000"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rest of your workflow stays familiar: open a terminal, &lt;code&gt;cd&lt;/code&gt; into a repository, run &lt;code&gt;claude&lt;/code&gt;, and ask for help.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Claude Code uses the Anthropic Messages API protocol. The terminal app decides where to send requests by reading &lt;code&gt;ANTHROPIC_BASE_URL&lt;/code&gt; and authenticates with &lt;code&gt;ANTHROPIC_AUTH_TOKEN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;ANTHROPIC_BASE_URL&lt;/code&gt; is set to &lt;code&gt;https://api.acedata.cloud&lt;/code&gt;, Claude Code sends requests to Ace Data Cloud. Ace Data Cloud checks the API token, routes the request to an available Claude Code service channel, and records usage after the request completes.&lt;/p&gt;

&lt;p&gt;No local proxy process is required. No wrapper CLI is required. You still run the official &lt;code&gt;claude&lt;/code&gt; command; the only change is the API target and token.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Install the Claude Code CLI
&lt;/h2&gt;

&lt;p&gt;Claude Code supports macOS, Linux, Windows, and WSL. The native installer is the simplest path because it installs the &lt;code&gt;claude&lt;/code&gt; command and handles updates.&lt;/p&gt;

&lt;p&gt;On macOS, Linux, or WSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://claude.ai/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;irm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://claude.ai/install.ps1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows CMD:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -fsSL https://claude.ai/install.cmd -o install.cmd &amp;amp;&amp;amp; install.cmd &amp;amp;&amp;amp; del install.cmd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you prefer npm and already have Node.js 18 or higher:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @anthropic-ai/claude-code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then reopen your terminal and check the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the shell says &lt;code&gt;command not found&lt;/code&gt;, reopen the terminal again or inspect the PATH message printed by the installer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Configure it globally for your shell
&lt;/h2&gt;

&lt;p&gt;If you want every project on your machine to use the same API route, put the configuration in your shell startup file: &lt;code&gt;~/.zshrc&lt;/code&gt;, &lt;code&gt;~/.bashrc&lt;/code&gt;, or &lt;code&gt;~/.bash_profile&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ACEDATACLOUD_API_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{token}"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://api.acedata.cloud"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_AUTH_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ACEDATACLOUD_API_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CLAUDE_CODE_AUTO_COMPACT_WINDOW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"850000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;{token}&lt;/code&gt; with your API token from the Ace Data Cloud console. The &lt;code&gt;CLAUDE_CODE_AUTO_COMPACT_WINDOW&lt;/code&gt; value sets the automatic compression trigger window to about 850,000 tokens. It reserves room for tool results and final answers; it does not change the model context limit.&lt;/p&gt;

&lt;p&gt;Reload the shell config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the matching file name if you configured Bash instead of Zsh.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Or configure only one repository
&lt;/h2&gt;

&lt;p&gt;For team projects, I usually prefer project-local configuration with a personal ignored file. Create &lt;code&gt;.claude/settings.local.json&lt;/code&gt; in the repository root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ANTHROPIC_AUTH_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{token}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ANTHROPIC_BASE_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.acedata.cloud"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"CLAUDE_CODE_AUTO_COMPACT_WINDOW"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"850000"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This only affects the current project. The same file is also read when the project is opened with the Claude Code VS Code extension.&lt;/p&gt;

&lt;p&gt;Do not commit personal credentials. Add this file to &lt;code&gt;.gitignore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/settings.local.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your shell already has an old &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt;, unset it in the same environment where you launch Claude Code or VS Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;unset &lt;/span&gt;ANTHROPIC_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important detail is to actually unset it, not set it to an empty string. After changing this, restart Claude Code. For VS Code, reload the window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Start a session and verify the route
&lt;/h2&gt;

&lt;p&gt;From a project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/your/project
claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A first prompt can be simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Explain the directory structure of this project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a one-off task, pass the prompt directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude &lt;span class="s2"&gt;"Help me check the recent git diff for possible bugs"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want Claude Code to print the result and exit, use &lt;code&gt;-p&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"Summarize the purpose of README.md"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the interactive Claude Code interface, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the auth source and base URL, including:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auth token: ANTHROPIC_AUTH_TOKEN
Anthropic base URL: https://api.acedata.cloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the base URL is not &lt;code&gt;https://api.acedata.cloud&lt;/code&gt;, the current terminal probably did not load the expected shell config, or a project-level config is overriding it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional: choose model tiers explicitly
&lt;/h2&gt;

&lt;p&gt;Claude Code can select models by task type, but the documented environment variables let you set defaults:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_DEFAULT_OPUS_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"claude-opus-4-8"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_DEFAULT_SONNET_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"claude-sonnet-4-6"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_DEFAULT_HAIKU_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"claude-haiku-4-5-20251001"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CLAUDE_CODE_SUBAGENT_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"claude-sonnet-4-6"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use these only if you know you want consistent model choices. Otherwise, the default Claude Code selection is a reasonable starting point.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical habit
&lt;/h2&gt;

&lt;p&gt;Keep global config for personal machines and use &lt;code&gt;.claude/settings.local.json&lt;/code&gt; when a repository needs a predictable setup. In both cases, verify with &lt;code&gt;/status&lt;/code&gt; before doing serious work. That one check catches most misconfigured shells, stale login state, and accidental credential conflicts.&lt;/p&gt;

&lt;p&gt;For the full Ace Data Cloud reference, see the Claude Code terminal setup guide: &lt;a href="https://platform.acedata.cloud/documents/claude-code-terminal-integration" rel="noopener noreferrer"&gt;https://platform.acedata.cloud/documents/claude-code-terminal-integration&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Fetch the Official MP4 for a Suno Track from an API</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Wed, 12 Aug 2026 01:15:06 +0000</pubDate>
      <link>https://dev.to/germey/how-to-fetch-the-official-mp4-for-a-suno-track-from-an-api-337a</link>
      <guid>https://dev.to/germey/how-to-fetch-the-official-mp4-for-a-suno-track-from-an-api-337a</guid>
      <description>&lt;p&gt;If your app generates music, the next practical problem is usually distribution: you may have a song ID, but your UI, CMS, or social workflow often needs a shareable MP4 asset.&lt;/p&gt;

&lt;p&gt;This guide shows a small but useful backend pattern: take a Suno-generated &lt;code&gt;audio_id&lt;/code&gt;, call the Suno MP4 endpoint through Ace Data Cloud, and store the returned &lt;code&gt;data.video_url&lt;/code&gt; for playback, publishing, or downstream processing.&lt;/p&gt;

&lt;p&gt;The API in this document is intentionally simple. That makes it a good example of how to wrap a media-generation step with a clean service boundary: one input, one POST request, one output URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do
&lt;/h2&gt;

&lt;p&gt;The Suno MP4 API lets you obtain the official generated MP4 link for a generated music track.&lt;/p&gt;

&lt;p&gt;The core details are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Endpoint: &lt;code&gt;POST https://api.acedata.cloud/suno/mp4&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Request format: JSON&lt;/li&gt;
&lt;li&gt;Auth header: &lt;code&gt;authorization: Bearer {token}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Content header: &lt;code&gt;content-type: application/json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Accept header: &lt;code&gt;accept: application/json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Required body field: &lt;code&gt;audio_id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Output field to persist: &lt;code&gt;data.video_url&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The request body has only one input parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"audio_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"275113ab-fe5c-4bca-a33c-0cca96b39fa6"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That simplicity is useful in production. You can treat MP4 fetching as a deterministic enrichment step after music generation: once a track has an official song ID, your worker asks for the corresponding MP4 and saves the returned URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;A typical backend flow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your app already has a Suno &lt;code&gt;audio_id&lt;/code&gt; from an earlier generation step.&lt;/li&gt;
&lt;li&gt;A worker calls &lt;code&gt;POST /suno/mp4&lt;/code&gt; with that ID.&lt;/li&gt;
&lt;li&gt;The API responds with &lt;code&gt;success&lt;/code&gt;, &lt;code&gt;task_id&lt;/code&gt;, &lt;code&gt;trace_id&lt;/code&gt;, and &lt;code&gt;data.video_url&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Your app stores &lt;code&gt;data.video_url&lt;/code&gt; next to the track record.&lt;/li&gt;
&lt;li&gt;The frontend uses that URL for preview, download, or a publishing workflow.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the documented Python request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.acedata.cloud/suno/mp4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accept&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer {token}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content-type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audio_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;275113ab-fe5c-4bca-a33c-0cca96b39fa6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful response looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"task_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"03ae7cca-c3a2-40a0-98b2-8f33426af438"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"trace_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"848d8d5a-d6bb-4e16-bb29-768c22cf1b3b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"video_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cdn1.suno.ai/275113ab-fe5c-4bca-a33c-0cca96b39fa6.mp4"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The field you normally want is &lt;code&gt;data.video_url&lt;/code&gt;. &lt;code&gt;task_id&lt;/code&gt; and &lt;code&gt;trace_id&lt;/code&gt; are also worth logging because they make support and debugging much easier if a job behaves unexpectedly.&lt;/p&gt;

&lt;h2&gt;
  
  
  A curl version for workers and scripts
&lt;/h2&gt;

&lt;p&gt;For a small queue worker, cron job, or shell-based test, the same request can be expressed with curl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.acedata.cloud/suno/mp4"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"accept: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: Bearer {token}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"content-type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "audio_id": "275113ab-fe5c-4bca-a33c-0cca96b39fa6"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I were integrating this into a service, I would keep the function tiny:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_suno_mp4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;audio_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.acedata.cloud/suno/mp4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audio_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;audio_id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accept&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content-type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;video_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps the rest of your codebase from knowing about endpoint paths or response shape. The rest of the app only asks for an MP4 URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this fits in a real product
&lt;/h2&gt;

&lt;p&gt;A common media app pipeline might have records like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"track_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"audio_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"275113ab-fe5c-4bca-a33c-0cca96b39fa6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"late night demo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mp4_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"audio_generated"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After calling the MP4 API, update the record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"track_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"audio_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"275113ab-fe5c-4bca-a33c-0cca96b39fa6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"late night demo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mp4_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cdn1.suno.ai/275113ab-fe5c-4bca-a33c-0cca96b39fa6.mp4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mp4_ready"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That small state transition unlocks a lot of product behavior: a preview button in an admin panel, a scheduled social post, an export queue, or a download link in a user dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical implementation notes
&lt;/h2&gt;

&lt;p&gt;Because the request has only one business field, most production issues will come from surrounding concerns rather than request construction.&lt;/p&gt;

&lt;p&gt;First, validate that &lt;code&gt;audio_id&lt;/code&gt; exists before sending the request. If your generation step is asynchronous, do not call the MP4 endpoint until the track has an official generated song ID.&lt;/p&gt;

&lt;p&gt;Second, do not expose your API token in frontend code. Keep the &lt;code&gt;authorization&lt;/code&gt; header on the server side and let your backend return only the MP4 URL or your own signed asset reference.&lt;/p&gt;

&lt;p&gt;Third, persist the full response metadata somewhere useful. Even if your main table only stores &lt;code&gt;mp4_url&lt;/code&gt;, logging &lt;code&gt;task_id&lt;/code&gt; and &lt;code&gt;trace_id&lt;/code&gt; gives you a clean audit trail.&lt;/p&gt;

&lt;p&gt;Finally, make the function idempotent at the application level. If a track already has an &lt;code&gt;mp4_url&lt;/code&gt;, your worker can skip the call unless you explicitly want to refresh it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small API, but a useful boundary
&lt;/h2&gt;

&lt;p&gt;The nice thing about this endpoint is that it does not try to do too much. It converts one known identifier, &lt;code&gt;audio_id&lt;/code&gt;, into one useful media asset, &lt;code&gt;data.video_url&lt;/code&gt;. That makes it easy to test, easy to retry, and easy to fit into an existing media pipeline.&lt;/p&gt;

&lt;p&gt;For the original field names and sample response, see the Ace Data Cloud documentation: &lt;a href="https://platform.acedata.cloud/documents/suno-mp4-integration" rel="noopener noreferrer"&gt;https://platform.acedata.cloud/documents/suno-mp4-integration&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>A Practical Guide to Adding Nano Banana Image Tools to Claude Desktop, VS Code, and Cursor</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Mon, 10 Aug 2026 01:04:13 +0000</pubDate>
      <link>https://dev.to/germey/a-practical-guide-to-adding-nano-banana-image-tools-to-claude-desktop-vs-code-and-cursor-1ilm</link>
      <guid>https://dev.to/germey/a-practical-guide-to-adding-nano-banana-image-tools-to-claude-desktop-vs-code-and-cursor-1ilm</guid>
      <description>&lt;p&gt;If your coding assistant can already read files, run commands, and reason about a project, the next useful step is often visual: generating mockups, editing product shots, or iterating on image assets without leaving the IDE.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do
&lt;/h2&gt;

&lt;p&gt;Nano Banana MCP is an MCP server for connecting image generation and image editing tools to AI clients such as Claude Desktop, VS Code, and Cursor. Once it is configured, the assistant can call a small set of image-focused tools during a normal conversation instead of forcing you to switch to a separate image UI.&lt;/p&gt;

&lt;p&gt;The documented tool surface is intentionally compact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;nanobanana_generate_image&lt;/code&gt; — generate images from text prompts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nanobanana_edit_image&lt;/code&gt; — edit or combine existing images&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nanobanana_get_task&lt;/code&gt; — query the status of one task&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nanobanana_get_tasks_batch&lt;/code&gt; — query multiple task statuses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The server supports the &lt;code&gt;nano-banana&lt;/code&gt;, &lt;code&gt;nano-banana-2&lt;/code&gt;, and &lt;code&gt;nano-banana-pro&lt;/code&gt; models. That makes it a good fit for builder workflows where you want to move from a text idea to an image, then keep refining that image in the same chat.&lt;/p&gt;

&lt;p&gt;Typical examples from the integration guide include prompts like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Help me generate a watercolor landscape painting"&lt;/li&gt;
&lt;li&gt;"Photoshop this clothing onto this person"&lt;/li&gt;
&lt;li&gt;"Place this product in a café scene"&lt;/li&gt;
&lt;li&gt;"Generate a high-quality portrait using the nano-banana-pro model"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those examples are useful because they show the real shape of the workflow: the user describes the image task in natural language, and the MCP client routes the request to the available Nano Banana tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;MCP, or Model Context Protocol, gives AI clients a standard way to call external tools. In this setup, the local MCP server is &lt;code&gt;mcp-nanobanana-pro&lt;/code&gt;. Your client starts that command, passes an Ace Data Cloud token through the &lt;code&gt;ACEDATACLOUD_API_TOKEN&lt;/code&gt; environment variable, and then exposes the Nano Banana tools to the assistant.&lt;/p&gt;

&lt;p&gt;The basic installation path is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;mcp-nanobanana-pro
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you prefer installing from source, the documented path is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/AceDataCloud/NanoBananaMCP.git
&lt;span class="nb"&gt;cd &lt;/span&gt;NanoBananaMCP
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installation, the command your client needs to run is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mcp-nanobanana-pro
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important thing is not to hard-code secrets in prompts or project files you plan to commit. Treat &lt;code&gt;ACEDATACLOUD_API_TOKEN&lt;/code&gt; like any other API token: keep it local, rotate it if needed, and avoid pasting it into public issues or screenshots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure Claude Desktop
&lt;/h2&gt;

&lt;p&gt;For Claude Desktop, edit the client configuration file. The documented locations are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;~/Library/Application Support/Claude/claude_desktop_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Windows: &lt;code&gt;%APPDATA%\\Claude\\claude_desktop_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add an MCP server named &lt;code&gt;nanobanana&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nanobanana"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp-nanobanana-pro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ACEDATACLOUD_API_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your API Token"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use &lt;code&gt;uvx&lt;/code&gt; and do not want to install the package in advance, the guide also documents this version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nanobanana"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"mcp-nanobanana-pro"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ACEDATACLOUD_API_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your API Token"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the file, restart Claude Desktop, and start with a small request. For example, ask it to generate a simple icon concept or edit one existing image. A small first test makes it easier to verify that the server starts correctly and that the token is available to the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure VS Code or Cursor
&lt;/h2&gt;

&lt;p&gt;For VS Code and Cursor, create &lt;code&gt;.vscode/mcp.json&lt;/code&gt; in the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nanobanana"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp-nanobanana-pro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ACEDATACLOUD_API_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your API Token"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;uvx&lt;/code&gt; version is similar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nanobanana"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"mcp-nanobanana-pro"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ACEDATACLOUD_API_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your API Token"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This project-level setup is nice when the visual workflow belongs to a specific repository. For example, a frontend repo might use it for hero image drafts, empty-state illustrations, or product-placement experiments. A docs repo might use it to generate tutorial covers and diagrams. Because the MCP config lives with the workspace, the assistant has the right tool available where the work happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical workflow to try
&lt;/h2&gt;

&lt;p&gt;Here is a simple builder-oriented loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask the assistant to draft a prompt for the image you need.&lt;/li&gt;
&lt;li&gt;Run generation with &lt;code&gt;nanobanana_generate_image&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Review the result in the chat or client output.&lt;/li&gt;
&lt;li&gt;Ask for a targeted edit with &lt;code&gt;nanobanana_edit_image&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;nanobanana_get_task&lt;/code&gt; if the client needs to check task progress.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, in a product UI project you might say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Generate a clean dashboard illustration for a dark-mode SaaS landing page. Use a minimal terminal panel, API cards, and a blue/green accent palette.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then follow up with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Edit the image so the API cards are less crowded and the terminal panel is more prominent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is where MCP feels useful: the same assistant that understands your implementation context can also help you iterate on visual assets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing notes
&lt;/h2&gt;

&lt;p&gt;Nano Banana MCP is not a replacement for design judgment, but it is a practical way to bring image generation and editing closer to where builders already work: Claude Desktop, VS Code, and Cursor. Start with a narrow use case, keep prompts specific, and treat the generated output as a draft you can refine.&lt;/p&gt;

&lt;p&gt;The full setup reference is in the Ace Data Cloud Nano Banana MCP documentation: &lt;a href="https://platform.acedata.cloud/documents/nano-banana-mcp" rel="noopener noreferrer"&gt;https://platform.acedata.cloud/documents/nano-banana-mcp&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Build a Talking-Photo Lip Sync Pipeline with Kling and an API</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Sun, 09 Aug 2026 01:03:52 +0000</pubDate>
      <link>https://dev.to/germey/how-to-build-a-talking-photo-lip-sync-pipeline-with-kling-and-an-api-345f</link>
      <guid>https://dev.to/germey/how-to-build-a-talking-photo-lip-sync-pipeline-with-kling-and-an-api-345f</guid>
      <description>&lt;p&gt;If you have ever tried to turn a still portrait into a short narrated video, the hard part is usually not generating the face motion — it is keeping the mouth movement aligned with the voice without stitching together a fragile video pipeline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Founrcjoq4m2z0qeowds7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Founrcjoq4m2z0qeowds7.png" alt="Kling Lip Sync API cover" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This guide walks through a small, practical pipeline using the Kling Lip Sync API on Ace Data Cloud. The goal is simple: start with a Kling-generated 5s or 10s video, then drive the character's mouth with either an audio file or a short text prompt.&lt;/p&gt;

&lt;p&gt;The useful part is that the lip sync step is just one JSON request, so you can wire it into a backend job, an internal content tool, or a prototype for digital-human narration without building a custom video stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do
&lt;/h2&gt;

&lt;p&gt;The endpoint is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST https://api.acedata.cloud/kling/lip-sync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It accepts &lt;code&gt;application/json&lt;/code&gt; and returns &lt;code&gt;application/json&lt;/code&gt;. You authenticate with a bearer token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;authorization: Bearer ${API_KEY}
content-type: application/json
accept: application/json
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two generation modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;audio2video&lt;/code&gt;: use an existing audio file to drive the mouth movement.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;text2video&lt;/code&gt;: provide short text and a voice ID, and let the API generate the speech and lip sync together.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the source video, you provide exactly one of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;video_id&lt;/code&gt;: the ID of a Kling-generated video, such as one returned by &lt;code&gt;/kling/videos&lt;/code&gt; with &lt;code&gt;image2video&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;video_url&lt;/code&gt;: a public URL to a video file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;video_id&lt;/code&gt; path is convenient if you are already using Kling to animate a still image. The docs note that the video must be 5s or 10s and generated within the last 30 days. If you use &lt;code&gt;video_url&lt;/code&gt;, the file must be &lt;code&gt;.mp4&lt;/code&gt; or &lt;code&gt;.mov&lt;/code&gt;, no more than 100MB, 2–10 seconds long, 720p or 1080p, and between 720 and 1920 pixels in dimensions.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Think of the API as a second pass after you already have a short character video.&lt;/p&gt;

&lt;p&gt;A complete talking-photo flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;still image -&amp;gt; /kling/videos image2video -&amp;gt; video_id -&amp;gt; /kling/lip-sync -&amp;gt; video_url
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first step creates motion from an image. The second step replaces or aligns the mouth motion so the subject speaks naturally.&lt;/p&gt;

&lt;p&gt;Here is the image-to-video step shown in the docs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'https://api.acedata.cloud/kling/videos'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'authorization: Bearer ${API_KEY}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "kling-v2-1-master",
    "action": "image2video",
    "start_image_url": "https://cdn.acedata.cloud/4hfydw.jpg",
    "prompt": "look at camera, natural",
    "duration": 5,
    "mode": "pro"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That request returns a &lt;code&gt;video_id&lt;/code&gt;, which becomes the input to the lip sync call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1: drive the video with audio
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;audio2video&lt;/code&gt; when you already have a voiceover, a TTS file, or a recorded narration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'https://api.acedata.cloud/kling/lip-sync'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'authorization: Bearer ${API_KEY}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "mode": "audio2video",
    "video_id": "895055164389466178",
    "audio_url": "https://cdn.acedata.cloud/6f7d62b18b.wav"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this mode, &lt;code&gt;audio_url&lt;/code&gt; is required when &lt;code&gt;audio_type&lt;/code&gt; is &lt;code&gt;url&lt;/code&gt;, which is also the default. Supported audio formats are &lt;code&gt;.mp3&lt;/code&gt;, &lt;code&gt;.wav&lt;/code&gt;, &lt;code&gt;.m4a&lt;/code&gt;, and &lt;code&gt;.aac&lt;/code&gt;, with a maximum file size of 5MB.&lt;/p&gt;

&lt;p&gt;A practical tip: keep the audio duration close to the video duration. The API can only work with the visual material you give it, so a 12-second narration is not a good fit for a 5-second clip.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2: drive the video with text
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;text2video&lt;/code&gt; when you want to produce a short line directly from text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'https://api.acedata.cloud/kling/lip-sync'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'authorization: Bearer ${API_KEY}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "mode": "text2video",
    "video_id": "895055164389466178",
    "text": "Hi, long time no see. I am doing well, take care of yourself.",
    "voice_id": "genshin_vindi2",
    "voice_language": "en",
    "voice_speed": 1.0
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;text2video&lt;/code&gt;, the required fields are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;text&lt;/code&gt;: the line to speak, up to 120 characters.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;voice_id&lt;/code&gt;: the voice to use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Optional voice controls include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;voice_language&lt;/code&gt;: &lt;code&gt;zh&lt;/code&gt; or &lt;code&gt;en&lt;/code&gt;, defaulting to &lt;code&gt;zh&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;voice_speed&lt;/code&gt;: from &lt;code&gt;0.8&lt;/code&gt; to &lt;code&gt;2.0&lt;/code&gt;, with one decimal place.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This mode is useful for UI previews, short product explainers, onboarding clips, or any workflow where the script is generated dynamically and you do not want to manage separate audio files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling the response
&lt;/h2&gt;

&lt;p&gt;A successful synchronous response includes the task ID, the new Kling video ID, and the final video URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"task_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"07a3ec65-9f7e-4a09-b7b7-282684082527"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"video_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"895055968777281546"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"video_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://platform2.cdn.acedata.cloud/kling/07a3ec65-9f7e-4a09-b7b7-282684082527.mp4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.966"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"succeed"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The returned &lt;code&gt;video_url&lt;/code&gt; is the artifact you can show in your app, hand off to an editor, or queue for another processing step. The returned &lt;code&gt;video_id&lt;/code&gt; is also reusable for later Kling operations such as another lip sync or extension step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Async mode for production jobs
&lt;/h2&gt;

&lt;p&gt;For a backend workflow, you probably do not want to hold a request open while a video job runs. The API supports async mode in two ways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"audio2video"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"video_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"895055164389466178"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"audio_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://your.cdn/voice.mp3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"async"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also provide &lt;code&gt;callback_url&lt;/code&gt;. Either option returns a &lt;code&gt;task_id&lt;/code&gt; immediately. To poll, call &lt;code&gt;/kling/tasks&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"retrieve"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;task_id&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A good production pattern is to store the original request, the &lt;code&gt;task_id&lt;/code&gt;, and the current state in your database. Then either process the callback or poll from a worker until the task reaches a terminal state.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few guardrails I would add
&lt;/h2&gt;

&lt;p&gt;Before sending a request, validate the constraints close to your UI or API boundary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Require exactly one of &lt;code&gt;video_id&lt;/code&gt; or &lt;code&gt;video_url&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For &lt;code&gt;text2video&lt;/code&gt;, reject text longer than 120 characters.&lt;/li&gt;
&lt;li&gt;For &lt;code&gt;audio2video&lt;/code&gt;, check that audio is no larger than 5MB.&lt;/li&gt;
&lt;li&gt;Prefer clear, frontal, single-person videos.&lt;/li&gt;
&lt;li&gt;Keep the script or audio length aligned with the clip length.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These small checks prevent the most common bad requests and make the workflow much easier to debug.&lt;/p&gt;

&lt;p&gt;If you want the exact field table and the latest constraints, the full Ace Data Cloud doc is here: &lt;a href="https://platform.acedata.cloud/documents/kling-lip-sync-integration" rel="noopener noreferrer"&gt;https://platform.acedata.cloud/documents/kling-lip-sync-integration&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>tutorial</category>
      <category>devtools</category>
    </item>
    <item>
      <title>How to Build an Audio-Driven Talking Photo Workflow with Dreamina</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Sat, 08 Aug 2026 01:13:49 +0000</pubDate>
      <link>https://dev.to/germey/how-to-build-an-audio-driven-talking-photo-workflow-with-dreamina-1jpe</link>
      <guid>https://dev.to/germey/how-to-build-an-audio-driven-talking-photo-workflow-with-dreamina-1jpe</guid>
      <description>&lt;p&gt;Turning a portrait and a voice recording into a short talking video sounds like a “demo day” feature, but the production details matter: public asset URLs, audio length limits, async job handling, and error traces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx3fi0nz6jb5d2oglyazz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx3fi0nz6jb5d2oglyazz.png" alt="Cover image for Dreamina Video Generation API" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This guide walks through the Dreamina Video Generation API on Ace Data Cloud from a builder’s point of view. We will send a portrait image plus a driving audio file, receive a synchronized speaking video, and decide when to use callbacks or polling instead of waiting on one long HTTP request.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do
&lt;/h2&gt;

&lt;p&gt;The Dreamina Video Generation API is designed for audio-driven talking-photo generation. You provide a portrait image via &lt;code&gt;image_url&lt;/code&gt;, a driving audio file via &lt;code&gt;audio_url&lt;/code&gt;, and optional steering text via &lt;code&gt;prompt&lt;/code&gt;. The endpoint generates a video where the person speaks with synchronized lips. The model defaults to &lt;code&gt;omnihuman-1.5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The core API shape is compact:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Endpoint: POST https://api.acedata.cloud/dreamina/videos
Authorization: Bearer &amp;lt;your API key&amp;gt;
Content-Type: application/json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The required request parameters are &lt;code&gt;image_url&lt;/code&gt; and &lt;code&gt;audio_url&lt;/code&gt;. Optional fields include &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;prompt&lt;/code&gt;, &lt;code&gt;mask_url&lt;/code&gt;, &lt;code&gt;callback_url&lt;/code&gt;, and &lt;code&gt;async&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The smallest useful request
&lt;/h2&gt;

&lt;p&gt;Here is a minimal JSON request body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"omnihuman-1.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/portrait.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"audio_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/voice.wav"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Natural speaking expression, stable face, calm presenter style"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is the same call as cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'https://api.acedata.cloud/dreamina/videos'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Authorization: Bearer &amp;lt;your API key&amp;gt;'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "omnihuman-1.5",
    "image_url": "https://example.com/portrait.jpg",
    "audio_url": "https://example.com/voice.wav",
    "prompt": "Natural speaking expression, stable face, calm presenter style"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The guide’s response example returns a platform-level &lt;code&gt;task_id&lt;/code&gt;, a &lt;code&gt;trace_id&lt;/code&gt;, and a nested &lt;code&gt;data&lt;/code&gt; object with the upstream task and media URLs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"task_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0c0b4d3a-2f1e-4a6b-9c2d-2b3c4d5e6f70"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"trace_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a9063166-26ed-4451-85b5-54e896817c69"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"task_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"362b4fed67bd11f1ad1100163e57d510"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"video_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cdn.acedata.cloud/634d760216.mp4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"image_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cdn.acedata.cloud/4hfydw.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"audio_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cdn.acedata.cloud/6f7d62b18b.wav"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For an app, &lt;code&gt;data.video_url&lt;/code&gt; is the artifact you show to the user. Keep &lt;code&gt;trace_id&lt;/code&gt; in logs because it is useful when debugging failed or slow jobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing the image and audio
&lt;/h2&gt;

&lt;p&gt;The API is only as good as the assets you send it. For the image, use a clear, well-lit, front-facing portrait. The face should be unobstructed and reasonably large in the frame. This is the kind of constraint you should enforce in your UI copy before users upload anything.&lt;/p&gt;

&lt;p&gt;For the audio, use a public mp3 or wav URL. The guide says to keep audio under 60 seconds, with 30 seconds or less recommended for 1080p and 60 seconds or less for 720p. Both &lt;code&gt;image_url&lt;/code&gt; and &lt;code&gt;audio_url&lt;/code&gt; must be reachable from the public internet.&lt;/p&gt;

&lt;p&gt;That last point is easy to miss. A signed URL that expires too quickly, a private S3 object, or a localhost link will fail. In a production flow, upload the portrait and audio to durable public URLs before calling the API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Synchronous, callback, or polling?
&lt;/h2&gt;

&lt;p&gt;By default, the endpoint runs synchronously and returns the finished video. That is convenient for short tests, but video generation can take long enough that you may not want to keep a web request open.&lt;/p&gt;

&lt;p&gt;The guide documents two async patterns. First, you can pass &lt;code&gt;callback_url&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/portrait.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"audio_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/voice.mp3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"callback_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/webhooks/dreamina-result"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this pattern, the endpoint returns a &lt;code&gt;task_id&lt;/code&gt; immediately and POSTs the result to your URL when ready.&lt;/p&gt;

&lt;p&gt;Second, you can pass &lt;code&gt;async: true&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/portrait.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"audio_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/voice.mp3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"async"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;async: true&lt;/code&gt;, the endpoint returns a &lt;code&gt;task_id&lt;/code&gt; immediately, and you poll the result with &lt;code&gt;POST /dreamina/tasks&lt;/code&gt; using either &lt;code&gt;task_id&lt;/code&gt; or &lt;code&gt;trace_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A simple production pattern is: create a local job row with status &lt;code&gt;pending&lt;/code&gt;, submit &lt;code&gt;/dreamina/videos&lt;/code&gt;, store &lt;code&gt;task_id&lt;/code&gt; and &lt;code&gt;trace_id&lt;/code&gt;, show a pending state, and mark the job &lt;code&gt;done&lt;/code&gt; when the webhook or polling result contains &lt;code&gt;data.video_url&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling common failures
&lt;/h2&gt;

&lt;p&gt;The documented errors are straightforward, but you should map them to actionable product states:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;400 bad_request&lt;/code&gt;: missing or invalid parameters such as &lt;code&gt;image_url&lt;/code&gt; or &lt;code&gt;audio_url&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;401 authorization_missing&lt;/code&gt; or &lt;code&gt;invalid_token&lt;/code&gt;: missing or invalid token&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;403 forbidden&lt;/code&gt;: insufficient balance or quota, or upstream not authorized&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;429 too_many_requests&lt;/code&gt;: rate limit exceeded&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;500 api_error&lt;/code&gt;: internal server error&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The error response includes &lt;code&gt;trace_id&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bad_request"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image_url is required (a public URL of a portrait image)"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"trace_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2efa9340-b21b-4e26-9e14-4aac95f343ab"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your client, branch on &lt;code&gt;error.code&lt;/code&gt;, show a human-readable message, and always log &lt;code&gt;trace_id&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical checklist
&lt;/h2&gt;

&lt;p&gt;Before you ship a talking-photo feature, I would add these checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;require a clear frontal portrait before upload&lt;/li&gt;
&lt;li&gt;validate that audio is mp3 or wav and under the duration limit&lt;/li&gt;
&lt;li&gt;upload both files to public URLs before calling the API&lt;/li&gt;
&lt;li&gt;keep &lt;code&gt;Authorization: Bearer &amp;lt;your API key&amp;gt;&lt;/code&gt; server-side&lt;/li&gt;
&lt;li&gt;store &lt;code&gt;task_id&lt;/code&gt;, &lt;code&gt;trace_id&lt;/code&gt;, input URLs, and &lt;code&gt;data.video_url&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;prefer &lt;code&gt;callback_url&lt;/code&gt; or &lt;code&gt;async: true&lt;/code&gt; for user-facing workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That gives you a small but reliable pipeline: portrait plus voice in, &lt;code&gt;video_url&lt;/code&gt; out, and enough task metadata to debug what happened.&lt;/p&gt;

&lt;p&gt;The maintained reference for fields, async behavior, and errors is here: &lt;a href="https://platform.acedata.cloud/documents/dreamina-videos-integration" rel="noopener noreferrer"&gt;https://platform.acedata.cloud/documents/dreamina-videos-integration&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>tutorial</category>
      <category>devtools</category>
    </item>
  </channel>
</rss>
