<?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 Adding MCP Servers to Codex CLI</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Fri, 11 Sep 2026 01:08:09 +0000</pubDate>
      <link>https://dev.to/germey/a-practical-guide-to-adding-mcp-servers-to-codex-cli-29ol</link>
      <guid>https://dev.to/germey/a-practical-guide-to-adding-mcp-servers-to-codex-cli-29ol</guid>
      <description>&lt;p&gt;Codex CLI is great at working inside a repository, but sometimes the useful thing it needs is outside the repo: a fresh web search, an image tool, a short-link helper, or a media workflow that should be called as a tool instead of described in text.&lt;/p&gt;

&lt;p&gt;That is where MCP is useful. Instead of asking a coding agent to explain how it would use an external capability, you wire that capability into the agent as a remote server. This guide walks through the practical configuration pattern for connecting Codex CLI to Ace Data Cloud MCP servers.&lt;/p&gt;

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

&lt;p&gt;Codex CLI uses the &lt;code&gt;~/.codex/config.toml&lt;/code&gt; configuration file for MCP server entries. Each remote MCP server gets a named section, a &lt;code&gt;url&lt;/code&gt;, and an &lt;code&gt;http_headers&lt;/code&gt; object containing the Bearer token.&lt;/p&gt;

&lt;p&gt;The documented Ace Data Cloud MCP servers include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Suno&lt;/code&gt; for music workflows: &lt;code&gt;https://suno.mcp.acedata.cloud/mcp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Flux&lt;/code&gt; for image generation and editing: &lt;code&gt;https://flux.mcp.acedata.cloud/mcp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Seedream&lt;/code&gt; for image generation and editing: &lt;code&gt;https://seedream.mcp.acedata.cloud/mcp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NanoBanana&lt;/code&gt; for Gemini-driven image workflows: &lt;code&gt;https://nanobanana.mcp.acedata.cloud/mcp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Luma&lt;/code&gt; for video workflows: &lt;code&gt;https://luma.mcp.acedata.cloud/mcp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Veo&lt;/code&gt; for video workflows: &lt;code&gt;https://veo.mcp.acedata.cloud/mcp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Seedance&lt;/code&gt; for video workflows: &lt;code&gt;https://seedance.mcp.acedata.cloud/mcp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Serp&lt;/code&gt; for Google web, image, and news search: &lt;code&gt;https://serp.mcp.acedata.cloud/mcp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ShortURL&lt;/code&gt; for single and batch short links: &lt;code&gt;https://shorturl.mcp.acedata.cloud/mcp&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You do not have to add everything. For a coding workflow, I would usually start with one search server and one artifact-oriented server, then add more only when the workflow actually needs them.&lt;/p&gt;

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

&lt;p&gt;An MCP entry in Codex CLI is just TOML. The important pieces are the server name, the remote URL, and the Authorization header. The source guide shows this shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mcp_servers.serp]&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://serp.mcp.acedata.cloud/mcp"&lt;/span&gt;
&lt;span class="py"&gt;http_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;"Authorization"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bearer yourToken"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;yourToken&lt;/code&gt; with your Ace Data Cloud token, save the file, then restart Codex CLI so the MCP servers are loaded again.&lt;/p&gt;

&lt;p&gt;For a small, practical setup, your &lt;code&gt;~/.codex/config.toml&lt;/code&gt; might start like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mcp_servers.serp]&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://serp.mcp.acedata.cloud/mcp"&lt;/span&gt;
&lt;span class="py"&gt;http_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;"Authorization"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bearer yourToken"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nn"&gt;[mcp_servers.flux]&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://flux.mcp.acedata.cloud/mcp"&lt;/span&gt;
&lt;span class="py"&gt;http_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;"Authorization"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bearer yourToken"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nn"&gt;[mcp_servers.shorturl]&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://shorturl.mcp.acedata.cloud/mcp"&lt;/span&gt;
&lt;span class="py"&gt;http_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;"Authorization"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bearer yourToken"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the agent three different kinds of help: search for context, image tooling for visual artifacts, and short links for sharing outputs. The exact tool names available to Codex will come from the remote MCP servers after they load.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the first configuration small
&lt;/h2&gt;

&lt;p&gt;It is tempting to paste every server into the config file at once. The overview document does provide entries for Suno, Flux, Seedream, NanoBanana, Luma, Veo, Seedance, Serp, and ShortURL, but a smaller first pass is easier to test.&lt;/p&gt;

&lt;p&gt;A good first test is search, because the expected behavior is easy to verify. After restarting Codex CLI, ask for something concrete like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use Serp to search for "OpenAI Codex CLI tutorial 2026" for the top 5 latest articles.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If Codex can see and call the MCP server, it should treat search as a tool call rather than guessing from its training data. That is the real value of this setup: the model stays in the coding loop, while current or specialized work moves to a tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add creative tools only when the repo needs them
&lt;/h2&gt;

&lt;p&gt;Once search works, add servers that match your actual project. If you are writing documentation, a useful workflow might be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use Flux to generate a GitHub PR cover image with the theme "Open Source Collaboration", 1280x640.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you are preparing a demo, the source guide gives another example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use Suno to write a 45-second calm piano piece for developer tutorial BGM.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those prompts are intentionally task-level. You are not hard-coding image or music parameters into your app; you are giving Codex an external capability it can call while you stay in the terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  A fuller config example
&lt;/h2&gt;

&lt;p&gt;When you are ready to expand, append only the servers you need. The documented pattern is consistent across servers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mcp_servers.suno]&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://suno.mcp.acedata.cloud/mcp"&lt;/span&gt;
&lt;span class="py"&gt;http_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;"Authorization"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bearer yourToken"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nn"&gt;[mcp_servers.nanobanana]&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://nanobanana.mcp.acedata.cloud/mcp"&lt;/span&gt;
&lt;span class="py"&gt;http_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;"Authorization"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bearer yourToken"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nn"&gt;[mcp_servers.veo]&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://veo.mcp.acedata.cloud/mcp"&lt;/span&gt;
&lt;span class="py"&gt;http_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;"Authorization"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bearer yourToken"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nn"&gt;[mcp_servers.seedance]&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://seedance.mcp.acedata.cloud/mcp"&lt;/span&gt;
&lt;span class="py"&gt;http_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;"Authorization"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bearer yourToken"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same token is used in the &lt;code&gt;Authorization&lt;/code&gt; header for each server. If something fails, first check the TOML syntax, the server URL, and whether the Bearer token was pasted correctly. Then restart Codex CLI again; configuration changes are only useful after the process reloads them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final notes
&lt;/h2&gt;

&lt;p&gt;MCP works best when you connect tools that have a clear job. For Codex CLI, that often means search for current context, image or video tools for developer-facing artifacts, and utility tools such as short links for publishing workflows.&lt;/p&gt;

&lt;p&gt;The complete Ace Data Cloud Codex MCP reference is here: &lt;a href="https://platform.acedata.cloud/documents/codex-mcp-all" rel="noopener noreferrer"&gt;https://platform.acedata.cloud/documents/codex-mcp-all&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>A Practical Guide to Using OpenCode IDE Extension with a Custom Provider</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Tue, 08 Sep 2026 01:06:52 +0000</pubDate>
      <link>https://dev.to/germey/a-practical-guide-to-using-opencode-ide-extension-with-a-custom-provider-1hp5</link>
      <guid>https://dev.to/germey/a-practical-guide-to-using-opencode-ide-extension-with-a-custom-provider-1hp5</guid>
      <description>&lt;p&gt;A coding assistant inside your editor is only useful if it can use the same model setup you already trust in the terminal.&lt;/p&gt;

&lt;p&gt;OpenCode is interesting because the IDE Extension does not need to be treated as a completely separate integration. According to the Ace Data Cloud OpenCode IDE guide, the extension connects to the local OpenCode runtime and reuses the same Provider configuration used by the CLI. That makes it a practical path if you want one model configuration to work across Terminal, TUI, and editor workflows.&lt;/p&gt;

&lt;p&gt;This guide walks through the setup pattern, the configuration file shape, and the minimal validation I would run before relying on it for real code edits.&lt;/p&gt;

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

&lt;p&gt;The OpenCode IDE Extension supports editors such as VS Code, Cursor, Windsurf, and VSCodium. The important boundary is that the extension reuses the OpenCode local runtime and Provider configuration; it does not mean the host editor itself has native Ace Data Cloud support.&lt;/p&gt;

&lt;p&gt;That distinction matters. A terminal chat request proving your Provider works is not the same thing as proving the IDE can read files, propose edits, or invoke tools correctly. Treat the first setup as a small integration test, not just a login check.&lt;/p&gt;

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

&lt;p&gt;The Provider configuration lives in &lt;code&gt;opencode.json&lt;/code&gt; or &lt;code&gt;opencode.jsonc&lt;/code&gt;. The documented setup uses the OpenAI-compatible AI SDK provider package and points it at the Ace Data Cloud API base URL.&lt;/p&gt;

&lt;p&gt;Here is the core configuration shape from the guide:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&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;"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;"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;"MODEL_ID"&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;"MODEL_ID"&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="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;"acedatacloud/MODEL_ID"&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 fields worth double-checking are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;provider.acedatacloud&lt;/code&gt;: the custom provider namespace used by OpenCode.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm&lt;/code&gt;: set to &lt;code&gt;@ai-sdk/openai-compatible&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;options.baseURL&lt;/code&gt;: set to &lt;code&gt;https://api.acedata.cloud/v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;options.apiKey&lt;/code&gt;: read from &lt;code&gt;{env:ACEDATACLOUD_API_KEY}&lt;/code&gt; instead of hardcoding a token.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;models.MODEL_ID&lt;/code&gt;: define the model ID you want to use.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;model&lt;/code&gt;: select it through the namespaced value &lt;code&gt;acedatacloud/MODEL_ID&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, I would commit the &lt;code&gt;opencode.jsonc&lt;/code&gt; structure if it is part of a shared dev environment, but keep the actual API token only in the developer or CI environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: configure the Provider first
&lt;/h2&gt;

&lt;p&gt;Start with the Provider before thinking about the editor. Create or update &lt;code&gt;opencode.jsonc&lt;/code&gt;, choose a real &lt;code&gt;MODEL_ID&lt;/code&gt; from the model catalog you plan to use, and export the token as 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;"your-token-here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run OpenCode from the terminal and perform a tiny smoke test. I like using a boring prompt because it makes failures obvious:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reply only OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the CLI cannot answer that, do not move on to the IDE Extension yet. Fix the Provider config first: base URL, environment variable name, and model ID are the likely places to check.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: install the IDE Extension
&lt;/h2&gt;

&lt;p&gt;Once the Provider works in the terminal, install the IDE Extension. The guide gives two paths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;opencode&lt;/code&gt; in the integrated terminal of the editor and follow the official process to install the Extension automatically.&lt;/li&gt;
&lt;li&gt;Or install it manually from the corresponding Marketplace.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After installation, reload the editor. That reload step is easy to skip, but it is often the difference between testing the current extension process and testing stale state from before the install.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: run minimal validation inside the editor
&lt;/h2&gt;

&lt;p&gt;Now run the same simple check from the OpenCode Extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reply only OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms that the extension can reach the local OpenCode runtime and that the runtime is using the configured Provider and model.&lt;/p&gt;

&lt;p&gt;But I would not stop there. The document explicitly warns that successful ordinary chat cannot replace validation for file reading, editing, and tool invocation. A minimal validation set could be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask the extension to summarize the currently open file.&lt;/li&gt;
&lt;li&gt;Ask it to propose a small edit without applying it.&lt;/li&gt;
&lt;li&gt;Ask it to apply a tiny, reversible change in a test file.&lt;/li&gt;
&lt;li&gt;If your workflow uses tools, run one tool-based request and record the result.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keep these tests intentionally small. The goal is not to benchmark intelligence; it is to verify the request chain between the editor, OpenCode runtime, Provider, and selected model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current boundaries to keep in mind
&lt;/h2&gt;

&lt;p&gt;There are three boundaries I would document for a team rollout.&lt;/p&gt;

&lt;p&gt;First, the Provider capability comes from the OpenCode runtime. The host editor is connecting to that runtime; it is not independently implementing Ace Data Cloud support.&lt;/p&gt;

&lt;p&gt;Second, VS Code and forks such as Cursor, Windsurf, and VSCodium may differ by extension version and request behavior. Record the editor name and extension version when you validate the setup.&lt;/p&gt;

&lt;p&gt;Third, validate the actual workflows you expect developers to use. Chat, file reading, edits, and tool invocation are different integration surfaces. Passing one does not automatically prove the others.&lt;/p&gt;

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

&lt;p&gt;For a single developer, the setup is just a few steps. For a team, I would make it repeatable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add an example &lt;code&gt;opencode.jsonc&lt;/code&gt; with &lt;code&gt;ACEDATACLOUD_API_KEY&lt;/code&gt; as an environment reference.&lt;/li&gt;
&lt;li&gt;Decide and document the &lt;code&gt;MODEL_ID&lt;/code&gt; value.&lt;/li&gt;
&lt;li&gt;Keep the token out of source control.&lt;/li&gt;
&lt;li&gt;Install or update the IDE Extension.&lt;/li&gt;
&lt;li&gt;Reload the editor.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;Reply only OK&lt;/code&gt; from the extension.&lt;/li&gt;
&lt;li&gt;Validate file reading and a safe edit in a test file.&lt;/li&gt;
&lt;li&gt;Record editor name, editor version, extension version, and model ID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That gives you a boring but reliable integration path: one Provider configuration, reused by the OpenCode runtime, then exercised through the IDE where developers actually work.&lt;/p&gt;

&lt;p&gt;For the full configuration notes and current boundaries, see the source guide: &lt;a href="https://platform.acedata.cloud/documents/opencode-ide-integration" rel="noopener noreferrer"&gt;OpenCode IDE Extension Setup Guide&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>A Practical Guide to the OpenAI Responses API with Python</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Mon, 07 Sep 2026 01:05:16 +0000</pubDate>
      <link>https://dev.to/germey/a-practical-guide-to-the-openai-responses-api-with-python-12e6</link>
      <guid>https://dev.to/germey/a-practical-guide-to-the-openai-responses-api-with-python-12e6</guid>
      <description>&lt;p&gt;If you are building an AI feature today, the hard part is rarely “send one prompt and print one answer”; it is handling messages, streaming, usage data, and multi-turn context cleanly.&lt;/p&gt;

&lt;p&gt;This guide walks through the OpenAI Responses API as exposed through Ace Data Cloud, using only the fields and examples from the public integration document. The goal is simple: make a minimal Python call, understand the response shape, then turn on streaming and multi-turn input without changing the mental model.&lt;/p&gt;

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

&lt;p&gt;The Responses API creates model responses from text or image input. The document describes a unified endpoint:&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/openai/responses
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Requests use JSON and bearer-token authentication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;authorization: Bearer {token}
content-type: application/json
accept: application/json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At minimum, the request needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;authorization&lt;/code&gt;: selected in the platform UI or passed as a bearer token in code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;model&lt;/code&gt;: for example, &lt;code&gt;gpt-4.1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;input&lt;/code&gt;: an array of messages, where each message has &lt;code&gt;role&lt;/code&gt; and &lt;code&gt;content&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The documented message roles are &lt;code&gt;user&lt;/code&gt;, &lt;code&gt;assistant&lt;/code&gt;, and &lt;code&gt;system&lt;/code&gt;. Common optional parameters include &lt;code&gt;max_tokens&lt;/code&gt;, &lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;n&lt;/code&gt;, &lt;code&gt;response_format&lt;/code&gt;, &lt;code&gt;tools&lt;/code&gt;, and &lt;code&gt;background&lt;/code&gt;. For real applications, the most immediately useful optional field is often &lt;code&gt;stream&lt;/code&gt;, because it lets a UI render output progressively instead of waiting for the full response.&lt;/p&gt;

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

&lt;p&gt;Think of the API as a response object factory. You send a model plus structured input, and it returns a response object with fields such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt;: the generated response task ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt;: for example, &lt;code&gt;completed&lt;/code&gt; or &lt;code&gt;in_progress&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;model&lt;/code&gt;: the model used for the response&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;output&lt;/code&gt;: the assistant message content&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;usage&lt;/code&gt;: token statistics for the request and response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A completed response in the document includes an &lt;code&gt;output&lt;/code&gt; array. Inside it, a message item contains &lt;code&gt;content&lt;/code&gt;, and the text itself appears under &lt;code&gt;content[].text&lt;/code&gt; with type &lt;code&gt;output_text&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That structure is useful because your application does not have to scrape plain text. You can store the full response object for debugging, read &lt;code&gt;usage.total_tokens&lt;/code&gt; for observability, and extract only the assistant text for display.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the smallest Python call
&lt;/h2&gt;

&lt;p&gt;Here is the basic Python shape from the documentation, adapted to use a placeholder token:&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/openai/responses&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;model&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;gpt-4.1&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;input&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="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;Hello&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="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;The important thing is the shape of &lt;code&gt;input&lt;/code&gt;. Even for one user message, it is still an array. That makes the jump to multi-turn conversation straightforward later.&lt;/p&gt;

&lt;p&gt;A typical completed response includes fields 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;"object"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"response"&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;"completed"&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;"gpt-4.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"output"&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;"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;"message"&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;"completed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&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;"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;"output_text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hello! How can I help you today?"&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;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&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;"usage"&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;"input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"output_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"total_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&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 a production app, I would avoid assuming &lt;code&gt;output[0]&lt;/code&gt; is always present. Check &lt;code&gt;status&lt;/code&gt;, check &lt;code&gt;error&lt;/code&gt;, then look for message items and &lt;code&gt;output_text&lt;/code&gt; content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add streaming for web interfaces
&lt;/h2&gt;

&lt;p&gt;For a chat UI, streaming makes the experience feel much better. The document enables this by adding &lt;code&gt;stream: True&lt;/code&gt; to the payload:&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/openai/responses&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;model&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;gpt-4.1&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;input&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="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;Hello&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;stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&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;The streaming response returns &lt;code&gt;data:&lt;/code&gt; lines. The document shows event types such as &lt;code&gt;response.created&lt;/code&gt;, &lt;code&gt;response.in_progress&lt;/code&gt;, &lt;code&gt;response.output_item.added&lt;/code&gt;, &lt;code&gt;response.output_text.delta&lt;/code&gt;, &lt;code&gt;response.output_text.done&lt;/code&gt;, and &lt;code&gt;response.completed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The key event for rendering incremental text is:&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;"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;"response.output_text.delta"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"delta"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hello"&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 end condition is also explicit: when the event &lt;code&gt;type&lt;/code&gt; is &lt;code&gt;response.completed&lt;/code&gt;, the stream has finished. That gives you a clean loop for frontend or backend streaming handlers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read each &lt;code&gt;data:&lt;/code&gt; line.&lt;/li&gt;
&lt;li&gt;Parse the JSON payload.&lt;/li&gt;
&lt;li&gt;Append &lt;code&gt;delta&lt;/code&gt; values from &lt;code&gt;response.output_text.delta&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Stop when &lt;code&gt;type&lt;/code&gt; becomes &lt;code&gt;response.completed&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Keep multi-turn context explicit
&lt;/h2&gt;

&lt;p&gt;Multi-turn dialogue is handled by sending multiple messages in the same &lt;code&gt;input&lt;/code&gt; array. The document’s example uses a previous user message, an assistant reply, and then a follow-up user question:&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="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;model&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;gpt-4.1&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;input&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="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;Hello&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="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;assistant&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;Hello! How can I help you today? 😊&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="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;What did I just say?&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is simple, but it is also a good architectural constraint. Your application owns the conversation state. You decide which previous messages to include, how much history to keep, and whether to summarize older turns before sending them again.&lt;/p&gt;

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

&lt;p&gt;A few details are worth making explicit before wiring this into an app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep the bearer token on the server side. Do not expose it in browser JavaScript.&lt;/li&gt;
&lt;li&gt;Log &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;model&lt;/code&gt;, and &lt;code&gt;usage&lt;/code&gt; for debugging and cost visibility.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;temperature&lt;/code&gt; only when you actually want more variation; the documented range is &lt;code&gt;0-2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;max_tokens&lt;/code&gt; when you need predictable response length.&lt;/li&gt;
&lt;li&gt;Treat streaming as a transport concern. The request shape stays almost the same; only &lt;code&gt;stream&lt;/code&gt; changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The nice thing about this API shape is that it scales from a “Hello” script to a real chat interface without introducing a second abstraction. You start with &lt;code&gt;model&lt;/code&gt; and &lt;code&gt;input&lt;/code&gt;, then add &lt;code&gt;stream&lt;/code&gt;, multi-turn messages, or optional fields as the product needs them.&lt;/p&gt;

&lt;p&gt;Full reference: &lt;a href="https://platform.acedata.cloud/documents/openai-v1-responses-integration" rel="noopener noreferrer"&gt;OpenAI Responses API Integration Guide&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <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>
  </channel>
</rss>
