<?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>How to Build an Image Generation and Editing Workflow with the Seedream Images API</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Sat, 01 Aug 2026 01:03:43 +0000</pubDate>
      <link>https://dev.to/germey/how-to-build-an-image-generation-and-editing-workflow-with-the-seedream-images-api-3eb</link>
      <guid>https://dev.to/germey/how-to-build-an-image-generation-and-editing-workflow-with-the-seedream-images-api-3eb</guid>
      <description>&lt;p&gt;If you have ever tried to add image generation to a real product, you know the hard part is not only the prompt — it is designing a request shape, handling long-running jobs, and returning a usable image URL to the rest of your app.&lt;/p&gt;

&lt;p&gt;In this guide, we will build a practical mental model for using the Seedream Images API through Ace Data Cloud. The goal is simple: send a prompt, optionally pass one or more input images for editing, and receive a generated image result that your backend can store, display, or pass to another workflow.&lt;/p&gt;

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

&lt;p&gt;The Seedream Images endpoint is useful when your application needs image generation or image editing from a structured API call. The documented endpoint is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Base URL: https://api.acedata.cloud
Endpoint: POST /seedream/images
Authorization: Bearer &amp;lt;token&amp;gt;
Content-Type: application/json
Accept: application/json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the smallest level, a generation request uses &lt;code&gt;action&lt;/code&gt;, &lt;code&gt;model&lt;/code&gt;, and &lt;code&gt;prompt&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;"doubao-seedream-5-0-260128"&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;"A photorealistic studio product shot of a frosted-glass perfume bottle on wet black slate, single softbox key light, water droplets, dark moody background, 85mm macro."&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 model string matters. For example, the docs use the complete model name &lt;code&gt;doubao-seedream-5-0-260128&lt;/code&gt;; abbreviations such as &lt;code&gt;doubao-seedream-5.0-lite&lt;/code&gt; are not accepted.&lt;/p&gt;

&lt;p&gt;The same API family also supports richer fields depending on the model you choose: &lt;code&gt;image&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;, &lt;code&gt;response_format&lt;/code&gt;, &lt;code&gt;watermark&lt;/code&gt;, &lt;code&gt;output_format&lt;/code&gt;, &lt;code&gt;async&lt;/code&gt;, &lt;code&gt;callback_url&lt;/code&gt;, and model-specific fields such as &lt;code&gt;seed&lt;/code&gt;, &lt;code&gt;guidance_scale&lt;/code&gt;, &lt;code&gt;stream&lt;/code&gt;, &lt;code&gt;sequential_image_generation&lt;/code&gt;, or &lt;code&gt;tools&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make your first request with curl
&lt;/h2&gt;

&lt;p&gt;Here is the basic curl request I would start with in a backend prototype:&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/seedream/images'&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": "doubao-seedream-5-0-260128",
    "prompt": "A photorealistic studio product shot of a frosted-glass perfume bottle on wet black slate, single softbox key light, water droplets, dark moody background, 85mm macro."
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful response includes a task identifier, a trace identifier, and a &lt;code&gt;data&lt;/code&gt; array with the image result:&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;"81246f86-05ff-4d7d-9553-1013e0c1cd32"&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;"ab50a78d-ab1f-457f-a46b-c2259cd5d35b"&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;"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;"A photorealistic studio product shot of a frosted-glass perfume bottle on wet black slate, single softbox key light, water droplets, dark moody background, 85mm macro."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2048x2048"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"image_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://platform2.cdn.acedata.cloud/seedream/901c6af6-e83a-4849-b233-295f6c20bacb.jpg"&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 application, the important fields are &lt;code&gt;success&lt;/code&gt;, &lt;code&gt;task_id&lt;/code&gt;, &lt;code&gt;trace_id&lt;/code&gt;, &lt;code&gt;data[].image_url&lt;/code&gt;, &lt;code&gt;data[].prompt&lt;/code&gt;, and &lt;code&gt;data[].size&lt;/code&gt;. I usually persist &lt;code&gt;task_id&lt;/code&gt; and &lt;code&gt;trace_id&lt;/code&gt; together, because they make debugging much easier when a user reports a missing image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose the right model and size
&lt;/h2&gt;

&lt;p&gt;The docs list several supported model strings, including &lt;code&gt;doubao-seedream-5-0-pro-260628&lt;/code&gt;, &lt;code&gt;doubao-seedream-5-0-260128&lt;/code&gt;, &lt;code&gt;doubao-seedream-4-5-251128&lt;/code&gt;, &lt;code&gt;doubao-seedream-4-0-250828&lt;/code&gt;, &lt;code&gt;doubao-seedream-3-0-t2i-250415&lt;/code&gt;, and &lt;code&gt;doubao-seededit-3-0-i2i-250628&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Not every field works with every model, so treat the model as part of your API contract. For example, &lt;code&gt;doubao-seedream-5-0-pro-260628&lt;/code&gt; is documented as a flagship single-image model and does not support &lt;code&gt;sequential_image_generation&lt;/code&gt;, &lt;code&gt;stream&lt;/code&gt;, or &lt;code&gt;tools&lt;/code&gt;. The &lt;code&gt;seed&lt;/code&gt; field is only supported by &lt;code&gt;doubao-seedream-3-0-t2i-250415&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;size&lt;/code&gt; field can be specified in two ways: a preset such as &lt;code&gt;1K&lt;/code&gt;, &lt;code&gt;2K&lt;/code&gt;, &lt;code&gt;3K&lt;/code&gt;, or &lt;code&gt;4K&lt;/code&gt; when the selected model supports presets, or an explicit pixel size such as &lt;code&gt;2048x2048&lt;/code&gt;. Preset support varies by model, so if you are building a UI, it is safer to make the allowed size options depend on the selected model rather than exposing one global dropdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  Editing an existing image
&lt;/h2&gt;

&lt;p&gt;For image editing, include the &lt;code&gt;image&lt;/code&gt; field with the URL or Base64 input image. The documentation says some Seedream models support single or multiple image inputs, while &lt;code&gt;doubao-seededit-3-0-i2i-250628&lt;/code&gt; supports only a single image input and &lt;code&gt;doubao-seedream-3-0-t2i-250415&lt;/code&gt; does not support the &lt;code&gt;image&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;A typical edit payload 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;"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;"doubao-seedream-4-0-250828"&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;"Keep the model pose and the liquid garment flowing shape unchanged. Change the clothing material from silver metal to completely transparent water (or glass). Through the liquid flow, the details of the model skin are visible. The light and shadow effect shifts from reflection to refraction."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image"&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="s2"&gt;"https://ark-project.tos-cn-beijing.volces.com/doc_image/seedream4_5_imageToimage.png"&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;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2K"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"watermark"&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="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 is the pattern I would use for product mockups, image variations, or controlled visual transformations: keep the source image in object storage, pass its URL in &lt;code&gt;image&lt;/code&gt;, and make the prompt describe only the intended edit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle long-running jobs cleanly
&lt;/h2&gt;

&lt;p&gt;Image generation can take about one to two minutes. If you do not want a client request to stay open, the API supports asynchronous handling.&lt;/p&gt;

&lt;p&gt;There are two documented approaches:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Provide &lt;code&gt;callback_url&lt;/code&gt;. The API returns a &lt;code&gt;task_id&lt;/code&gt;, then POSTs the final JSON result to your callback URL when the job finishes.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;async&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; without a callback URL. The API returns a &lt;code&gt;task_id&lt;/code&gt;, and your application polls &lt;code&gt;/seedream/tasks&lt;/code&gt; to fetch the final result.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A simple production pattern is to create an internal &lt;code&gt;image_jobs&lt;/code&gt; table with columns like &lt;code&gt;task_id&lt;/code&gt;, &lt;code&gt;trace_id&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;prompt&lt;/code&gt;, and &lt;code&gt;image_url&lt;/code&gt;. Your initial request creates the row. Your callback handler or polling worker updates it when &lt;code&gt;data[].image_url&lt;/code&gt; arrives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error handling
&lt;/h2&gt;

&lt;p&gt;The docs show errors such as &lt;code&gt;400 token_mismatched&lt;/code&gt;, &lt;code&gt;400 api_not_implemented&lt;/code&gt;, &lt;code&gt;401 invalid_token&lt;/code&gt;, &lt;code&gt;429 too_many_requests&lt;/code&gt;, and &lt;code&gt;500 api_error&lt;/code&gt;. A typical error response includes &lt;code&gt;success: false&lt;/code&gt;, an &lt;code&gt;error.code&lt;/code&gt;, an &lt;code&gt;error.message&lt;/code&gt;, and a &lt;code&gt;trace_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In practice, I would map these into three buckets: fix the request, refresh or check credentials, and retry later. Always log &lt;code&gt;trace_id&lt;/code&gt;; it is the field you will want when debugging a failed generation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this fits
&lt;/h2&gt;

&lt;p&gt;This is not just a “generate me a picture” endpoint. With &lt;code&gt;prompt&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, model-specific size handling, async execution, callbacks, and traceable task IDs, you can wire Seedream into a real builder workflow: product imagery, internal creative tooling, design previews, or background asset generation.&lt;/p&gt;

&lt;p&gt;The full API reference is available in the Ace Data Cloud documentation: &lt;a href="https://platform.acedata.cloud/documents/seedream-images-integration" rel="noopener noreferrer"&gt;Seedream Images API integration guide&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>tutorial</category>
      <category>devtools</category>
    </item>
    <item>
      <title>How to Generate Short Videos from Claude.ai with a Seedance MCP Connector</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Fri, 31 Jul 2026 01:11:50 +0000</pubDate>
      <link>https://dev.to/germey/how-to-generate-short-videos-from-claudeai-with-a-seedance-mcp-connector-4dna</link>
      <guid>https://dev.to/germey/how-to-generate-short-videos-from-claudeai-with-a-seedance-mcp-connector-4dna</guid>
      <description>&lt;p&gt;Sometimes the fastest way to prototype a video idea is not to open a separate generation dashboard, but to let your assistant create and poll the task while you stay in the conversation.&lt;/p&gt;

&lt;p&gt;This guide shows how the Seedance MCP connector works inside Claude.ai, based on the Ace Data Cloud documentation. The workflow is practical: add one remote MCP URL, authorize with OAuth, ask Claude to list available models and resolutions, then generate a short video and poll the task until a URL is returned.&lt;/p&gt;

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

&lt;p&gt;Seedance is ByteDance’s Doubao video generation family, exposed here through a remote MCP server. Once connected to Claude.ai, the documented tools include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;seedance_generate_video&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate a video from a text prompt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;seedance_generate_video_from_image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Animate an image into a video&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;seedance_get_task&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Query a single generation task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;seedance_get_tasks_batch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Query multiple tasks together&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;seedance_list_actions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List supported actions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;seedance_list_models&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List available models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;seedance_list_resolutions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List valid resolutions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The important part is that Claude can call the list tools first, choose valid values, submit a task, and then poll with &lt;code&gt;seedance_get_task&lt;/code&gt;. You do not need to manually copy model names between pages.&lt;/p&gt;

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

&lt;p&gt;Claude.ai supports custom MCP connectors. For Seedance, the connector uses OAuth direct connection, so the guide does not require copying an API token into Claude. You add a custom connector with this URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://seedance.mcp.acedata.cloud/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Claude.ai, open settings for connectors, add a custom connector, and use a name 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;Seedance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then fill the URL field with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://seedance.mcp.acedata.cloud/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After clicking connect, Claude redirects to Ace Data Cloud for OAuth authorization. Once approved, the connector appears in Claude with the Seedance tools listed above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set tool approval intentionally
&lt;/h2&gt;

&lt;p&gt;Video generation usually involves more than one tool call. A careful prompt may ask Claude to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;call &lt;code&gt;seedance_list_models&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;call &lt;code&gt;seedance_list_resolutions&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;submit with &lt;code&gt;seedance_generate_video&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;poll with &lt;code&gt;seedance_get_task&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The source guide recommends setting tool approval to “Always allow” for the connector if you are comfortable with the workflow. Otherwise, Claude may ask for confirmation several times during one generation. If you are testing a new prompt or model, leaving approvals on can still be a reasonable safety choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run a minimal text-to-video test
&lt;/h2&gt;

&lt;p&gt;A good first prompt should be specific about cost, duration, and resolution constraints without pretending you already know every valid option. The documentation uses this style:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use Seedance to generate one short video. First list models and resolutions, pick the FASTEST/CHEAPEST model (e.g. lite/fast variant) and the LOWEST resolution. Use the SHORTEST duration (4-5 seconds). Subject: a fluffy white kitten chasing a red leaf falling in a sunny garden, side view, simple background.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the documented run, Claude first called &lt;code&gt;seedance_list_models&lt;/code&gt; and &lt;code&gt;seedance_list_resolutions&lt;/code&gt;, then selected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;doubao-seedance-1-0-pro-fast-251015
480p
4 seconds
16:9
24fps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it submitted the generation with &lt;code&gt;seedance_generate_video&lt;/code&gt; and used &lt;code&gt;seedance_get_task&lt;/code&gt; to wait for the result.&lt;/p&gt;

&lt;p&gt;That order is worth copying. Instead of hard-coding values that may become stale, let the assistant ask the MCP server for valid options first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Animate an existing image
&lt;/h2&gt;

&lt;p&gt;The same connector also supports image-to-video generation. The documented tool for that is:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;A prompt can be as direct as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Take this image https://cdn.acedata.cloud/foo.png and animate it with Seedance fast model: slow zoom-in, soft wind, 4 seconds, 480p.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful for product mockups, social clips, or turning a static storyboard frame into a short motion test. The important fields to keep explicit are the source image URL, motion direction, duration, and resolution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compare variants before committing
&lt;/h2&gt;

&lt;p&gt;When you are not sure which model variant fits a scene, use Claude as a small experiment runner. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generate the same prompt "a samurai walking through bamboo forest in mist" with Seedance pro AND fast variants at 720p, 5 seconds, and tell me which one looks better.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude can call &lt;code&gt;seedance_list_models&lt;/code&gt;, submit separate tasks, then compare the returned videos. This is especially useful during prompt exploration, where you care more about learning what works than producing the final asset in one shot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Batch a few lightweight ideas
&lt;/h2&gt;

&lt;p&gt;For early ideation, batch generation can save time. The documented batch-style prompt is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generate 3 Seedance videos in parallel: 1) puppy on beach, 2) hot air balloon at sunrise, 3) coffee being poured. All 480p, 4s, fast model. Then return all three URLs.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here Claude can submit multiple tasks and use:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;to check them together. I would keep batch prompts small at first: three short clips are easier to review than ten, and the feedback loop stays manageable.&lt;/p&gt;

&lt;h2&gt;
  
  
  A builder-friendly workflow
&lt;/h2&gt;

&lt;p&gt;A practical workflow for short-form video prototyping looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with 480p and 4 seconds.&lt;/li&gt;
&lt;li&gt;Ask Claude to list models and resolutions before choosing values.&lt;/li&gt;
&lt;li&gt;Generate one simple shot with clear subject, motion, background, and framing.&lt;/li&gt;
&lt;li&gt;Review the result and change only one variable at a time.&lt;/li&gt;
&lt;li&gt;Batch two or three variants once the prompt direction is stable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This keeps the assistant useful without turning the process into a black box. You still decide the creative direction; the MCP connector handles valid parameters, task submission, and polling.&lt;/p&gt;

&lt;p&gt;Full setup reference: &lt;a href="https://platform.acedata.cloud/documents/claude-mcp-seedance" rel="noopener noreferrer"&gt;https://platform.acedata.cloud/documents/claude-mcp-seedance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>tutorial</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>How to Poll Async Image Tasks with the OpenAI Tasks API</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Thu, 30 Jul 2026 01:08:36 +0000</pubDate>
      <link>https://dev.to/germey/how-to-poll-async-image-tasks-with-the-openai-tasks-api-3a72</link>
      <guid>https://dev.to/germey/how-to-poll-async-image-tasks-with-the-openai-tasks-api-3a72</guid>
      <description>&lt;p&gt;When an image request takes longer than a comfortable HTTP timeout, the cleanest user experience is not to keep the browser waiting — it is to submit the job, store a task ID, and let your backend check the result later.&lt;/p&gt;

&lt;p&gt;That is the job of the OpenAI Tasks API in Ace Data Cloud: it lets you retrieve tasks created by image requests that were submitted in callback mode.&lt;/p&gt;

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

&lt;p&gt;The Tasks API is designed for asynchronous image workflows. You use it when an original image generation or editing request includes a &lt;code&gt;callback_url&lt;/code&gt;. In that mode, the image interface accepts the request and immediately returns a &lt;code&gt;task_id&lt;/code&gt;. Later, you can query the task by &lt;code&gt;id&lt;/code&gt;, or by &lt;code&gt;trace_id&lt;/code&gt; if you passed your own custom tracking identifier.&lt;/p&gt;

&lt;p&gt;The key details are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Endpoint: POST https://api.acedata.cloud/openai/tasks
Headers:
  accept: application/json
  authorization: Bearer {token}
  content-type: application/json
Supported actions:
  retrieve
  retrieve_batch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is one important constraint: task records are only persisted when the original image request includes &lt;code&gt;callback_url&lt;/code&gt;. Synchronous, non-callback calls do not create queryable task records.&lt;/p&gt;

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

&lt;p&gt;Think of the flow as a small job system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your app submits an image request with a &lt;code&gt;callback_url&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The image endpoint immediately returns a &lt;code&gt;task_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You store that task ID with your internal job record.&lt;/li&gt;
&lt;li&gt;Your worker or backend calls &lt;code&gt;POST /openai/tasks&lt;/code&gt; with &lt;code&gt;action: retrieve&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When the task has a &lt;code&gt;response&lt;/code&gt;, you read the final image URL from &lt;code&gt;response.data[]&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A returned task can include fields such as &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;trace_id&lt;/code&gt;, &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;application_id&lt;/code&gt;, &lt;code&gt;user_id&lt;/code&gt;, &lt;code&gt;credential_id&lt;/code&gt;, &lt;code&gt;created_at&lt;/code&gt;, &lt;code&gt;finished_at&lt;/code&gt;, &lt;code&gt;duration&lt;/code&gt;, &lt;code&gt;request&lt;/code&gt;, and &lt;code&gt;response&lt;/code&gt;. That is enough to build both a user-facing status page and an internal debugging trail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrieve one task by task ID
&lt;/h2&gt;

&lt;p&gt;For most apps, the simplest approach is to use the task ID returned by the original submission response. You do not need to invent a custom &lt;code&gt;trace_id&lt;/code&gt; unless you want to map tasks to your own business identifier.&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/openai/tasks'&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": "retrieve",
    "id": "7489df4c-ef03-4de0-b598-e9a590793434"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the task exists and has completed, the response follows this shape:&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;"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;"7489df4c-ef03-4de0-b598-e9a590793434"&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;"my-custom-trace-001"&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;"images"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1763142607.967&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"finished_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1763142637.404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;29.437&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"request"&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;"gpt-image-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;"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;"A cat sitting on a table"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1024x1024"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"callback_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://your.server/callback"&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;"response"&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;"created"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1763142637&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;"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/openai/...png"&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;"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="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If no task matches, the API returns an empty object:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That empty-object behavior is useful in a polling loop: treat it as “not found or not available yet,” not as a completed image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Submit and poll from Python
&lt;/h2&gt;

&lt;p&gt;Here is the end-to-end shape for a backend worker. The image request includes &lt;code&gt;callback_url&lt;/code&gt;, so the task is persisted and can be queried later.&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;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;API&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="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;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;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;ACEDATA_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&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;submit&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/openai/images/generations&lt;/span&gt;&lt;span class="sh"&gt;"&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="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;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-image-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;prompt&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;A watercolor style cat sitting on a table&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;callback_url&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;https://webhook.site/your-uuid&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="nf"&gt;json&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;submitted:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;task_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;submit&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="k"&gt;while&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;task&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/openai/tasks&lt;/span&gt;&lt;span class="sh"&gt;"&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="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;action&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;retrieve&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;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;task_id&lt;/span&gt;&lt;span class="p"&gt;},&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;if&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;task&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;response&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;finished:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;break&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;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a real app, I would not keep this loop inside a request handler. Put it in a worker, queue job, scheduled function, or background task. Store &lt;code&gt;task_id&lt;/code&gt;, the submitted prompt, the user ID in your own system, and the final &lt;code&gt;response.data[]&lt;/code&gt; when it arrives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Batch query for dashboards and recovery
&lt;/h2&gt;

&lt;p&gt;The second action is &lt;code&gt;retrieve_batch&lt;/code&gt;. It is useful when you need to recover multiple jobs, build an admin dashboard, or query by your own tracking IDs.&lt;/p&gt;

&lt;p&gt;The request can include fields such as &lt;code&gt;ids&lt;/code&gt;, &lt;code&gt;trace_ids&lt;/code&gt;, &lt;code&gt;application_id&lt;/code&gt;, &lt;code&gt;user_id&lt;/code&gt;, &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;offset&lt;/code&gt;, &lt;code&gt;limit&lt;/code&gt;, &lt;code&gt;created_at_min&lt;/code&gt;, and &lt;code&gt;created_at_max&lt;/code&gt;. At least one lookup dimension is required: for example &lt;code&gt;ids&lt;/code&gt;, &lt;code&gt;trace_ids&lt;/code&gt;, &lt;code&gt;application_id&lt;/code&gt;, &lt;code&gt;user_id&lt;/code&gt;, or a created-at time window.&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/openai/tasks'&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": "retrieve_batch",
    "trace_ids": ["my-trace-001", "my-trace-002"]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A batch response contains &lt;code&gt;items&lt;/code&gt; and &lt;code&gt;count&lt;/code&gt;, so your UI can render a task table without designing another aggregation layer first.&lt;/p&gt;

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

&lt;p&gt;A few details make this flow much easier to operate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Save both &lt;code&gt;task_id&lt;/code&gt; and &lt;code&gt;trace_id&lt;/code&gt; when available.&lt;/li&gt;
&lt;li&gt;Treat &lt;code&gt;finished_at&lt;/code&gt; as the signal that a task has reached a terminal state.&lt;/li&gt;
&lt;li&gt;Read final images from &lt;code&gt;response.data[]&lt;/code&gt;, not from the original submission payload.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;duration&lt;/code&gt; for basic latency monitoring.&lt;/li&gt;
&lt;li&gt;Remember that old task records may be cleared after the platform retention period.&lt;/li&gt;
&lt;li&gt;Poll gently. The Tasks interface itself does not incur charges, but your worker should still avoid noisy loops.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern keeps the image-generation UX predictable: users submit work, your system tracks the task, and the final image appears when the callback or polling result is ready.&lt;/p&gt;

&lt;p&gt;For the complete field reference, see the &lt;a href="https://platform.acedata.cloud/documents/openai-tasks-integration" rel="noopener noreferrer"&gt;OpenAI Tasks API Integration Guide&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>tutorial</category>
      <category>devtools</category>
    </item>
    <item>
      <title>How to Upload Files to a CDN Before Calling AI Media APIs</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Tue, 28 Jul 2026 01:07:14 +0000</pubDate>
      <link>https://dev.to/germey/how-to-upload-files-to-a-cdn-before-calling-ai-media-apis-372l</link>
      <guid>https://dev.to/germey/how-to-upload-files-to-a-cdn-before-calling-ai-media-apis-372l</guid>
      <description>&lt;p&gt;When you build AI media workflows, you quickly run into a small but annoying problem: many APIs want a public file URL, while your source asset is sitting on a local disk, in a temporary upload, or inside your own backend.&lt;/p&gt;

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

&lt;p&gt;The AceDataCloud platform file upload API is a small utility endpoint for turning a local file into a permanently accessible CDN URL. The uploaded file returns a &lt;code&gt;file_url&lt;/code&gt; such as:&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;"file_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cdn.acedata.cloud/qrd7gw.jpg"&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 URL can then be passed into downstream APIs that expect URL inputs, for example fields like &lt;code&gt;image_url&lt;/code&gt;, &lt;code&gt;image_urls&lt;/code&gt;, or &lt;code&gt;reference_url&lt;/code&gt; in media generation workflows.&lt;/p&gt;

&lt;p&gt;This is not a general marketing feature; it is a practical glue step. If your pipeline needs to call an image, video, or music API that cannot read your local file directly, you upload once, get a URL, and pass that URL forward.&lt;/p&gt;

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

&lt;p&gt;The upload endpoint belongs to the AceDataCloud Platform Management API, whose unified prefix is:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



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

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

&lt;/div&gt;



&lt;p&gt;The important request details are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication: account token in &lt;code&gt;Authorization: Bearer ...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Content type: &lt;code&gt;multipart/form-data&lt;/code&gt;, not JSON&lt;/li&gt;
&lt;li&gt;Form field: &lt;code&gt;file&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Default single file limit: 28 MB&lt;/li&gt;
&lt;li&gt;Response field: &lt;code&gt;file_url&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A minimal cURL request 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="s1"&gt;'https://platform.acedata.cloud/api/v1/files/'&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 platform-v1-92eb****629c'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s1"&gt;'file=@./photo.jpg'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response is intentionally small:&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;"file_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cdn.acedata.cloud/qrd7gw.jpg"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The returned CDN URL is publicly accessible without authentication. That makes it convenient for machine-to-machine workflows, but it also means you should not upload sensitive data unless you have a clear retention plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case: prepare a base image for a generation pipeline
&lt;/h2&gt;

&lt;p&gt;A common pattern is to let a user upload an image, store it briefly in your backend, then pass it to a generation API as a reference. Many APIs do not want base64 in those fields; they want a URL.&lt;/p&gt;

&lt;p&gt;The platform file upload endpoint solves the transition:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receive &lt;code&gt;photo.jpg&lt;/code&gt; from the user or from an internal job.&lt;/li&gt;
&lt;li&gt;Upload it to &lt;code&gt;POST /api/v1/files/&lt;/code&gt; as &lt;code&gt;multipart/form-data&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Read &lt;code&gt;data.file_url&lt;/code&gt; from the response.&lt;/li&gt;
&lt;li&gt;Use that URL in the next API call.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The documentation gives examples such as using CDN URLs with Midjourney &lt;code&gt;image_urls&lt;/code&gt;, Flux &lt;code&gt;image_url&lt;/code&gt;, and Veo &lt;code&gt;reference_url&lt;/code&gt;. The upload step is the same regardless of which downstream service you call next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Python example
&lt;/h2&gt;

&lt;p&gt;Here is the Python version from the same workflow, adapted as a small function. Notice that the request uses &lt;code&gt;files=...&lt;/code&gt;, not a JSON body.&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;PLATFORM_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;platform-v1-92eb****629c&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload_to_cdn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&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="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;resp&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://platform.acedata.cloud/api/v1/files/&lt;/span&gt;&lt;span class="sh"&gt;"&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;PLATFORM_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="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;files&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;file&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;photo.jpg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&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/jpeg&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="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;resp&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&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;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;file_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;file_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;upload_to_cdn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;photo.jpg&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;CDN 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;file_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a real service, you would keep &lt;code&gt;PLATFORM_TOKEN&lt;/code&gt; in your secret manager or environment, not inside source code. The token shown above is a masked example from the docs.&lt;/p&gt;

&lt;p&gt;Once you have &lt;code&gt;file_url&lt;/code&gt;, the next request can reference it directly. The docs show the idea with a downstream image 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="c1"&gt;# requests.post(
#     "https://api.acedata.cloud/midjourney/imagine",
#     json={"prompt": "...", "image_urls": [file_url]},
#     ...
# )
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key point is not the specific downstream model. The important contract is: local binary file in, stable CDN URL out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Node.js example
&lt;/h2&gt;

&lt;p&gt;For Node.js, use &lt;code&gt;form-data&lt;/code&gt; and stream the file from disk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form-data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;form&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./photo.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://platform.acedata.cloud/api/v1/files/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bearer platform-v1-92eb****629c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHeaders&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;r&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CDN URL:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;file_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One detail that often causes bugs: do not manually set &lt;code&gt;Content-Type: multipart/form-data&lt;/code&gt; without the boundary. Libraries such as &lt;code&gt;form-data&lt;/code&gt; generate the correct headers for you through &lt;code&gt;form.getHeaders()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error handling you should implement
&lt;/h2&gt;

&lt;p&gt;There are three practical failures to handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;400&lt;/code&gt; with &lt;code&gt;{"error":"No file provided"}&lt;/code&gt;: the request did not include the &lt;code&gt;file&lt;/code&gt; field, or the field name was wrong.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;401&lt;/code&gt;: the account token is missing or invalid.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;413&lt;/code&gt; with &lt;code&gt;{"error":"File too large","max_bytes":...}&lt;/code&gt;: the file exceeds the default 28 MB limit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a backend service, I would treat these differently. A &lt;code&gt;400&lt;/code&gt; is usually a developer or form-data bug. A &lt;code&gt;401&lt;/code&gt; should alert you to configuration or secret rotation. A &lt;code&gt;413&lt;/code&gt; is a product constraint: validate file size before upload and show the user a clear message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operational tips
&lt;/h2&gt;

&lt;p&gt;A few details matter in production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The returned &lt;code&gt;file_url&lt;/code&gt; is permanent and publicly accessible, so avoid uploading secrets, private user files, or unreleased assets unless that is intended.&lt;/li&gt;
&lt;li&gt;For bulk uploads, the docs recommend 2–4 concurrent uploads.&lt;/li&gt;
&lt;li&gt;A single connection can reach around 10 MB/s, which is usually enough for image and short media preprocessing.&lt;/li&gt;
&lt;li&gt;Because the endpoint expects a single &lt;code&gt;file&lt;/code&gt; field, keep batch logic in your client rather than trying to send many unrelated files in one request.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;This endpoint is useful because it removes a boring infrastructure step. Instead of standing up temporary object storage just to satisfy URL-based AI APIs, you can upload a file, receive &lt;code&gt;file_url&lt;/code&gt;, and continue the workflow.&lt;/p&gt;

&lt;p&gt;If you want the exact request and response reference, see the &lt;a href="https://platform.acedata.cloud/documents/platform-file-upload" rel="noopener noreferrer"&gt;platform file upload documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>tutorial</category>
      <category>webdev</category>
      <category>devtools</category>
    </item>
    <item>
      <title>How to Upload Reference Audio for a Suno-Based Music Workflow</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Mon, 27 Jul 2026 01:07:31 +0000</pubDate>
      <link>https://dev.to/germey/how-to-upload-reference-audio-for-a-suno-based-music-workflow-39m</link>
      <guid>https://dev.to/germey/how-to-upload-reference-audio-for-a-suno-based-music-workflow-39m</guid>
      <description>&lt;p&gt;When you build music features, the first useful primitive is often not “generate a full song from scratch.” It is: take an existing reference track, upload it safely, extract the metadata you need, and keep the returned song ID for the next step in your workflow.&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%2Fw53dk7nfv0x47qa2671l.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%2Fw53dk7nfv0x47qa2671l.png" alt="Cover image for Suno Upload API" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This guide walks through the Suno Upload Reference Audio API on Ace Data Cloud. The API is intentionally small: send a publicly accessible MP3 URL, receive an uploaded audio record, and use the returned &lt;code&gt;audio_id&lt;/code&gt; later for secondary creation.&lt;/p&gt;

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

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Base URL: https://api.acedata.cloud
Endpoint: POST /suno/upload
Headers:
  accept: application/json
  authorization: Bearer {token}
  content-type: application/json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request body has 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_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cdn.acedata.cloud/suno_demo.mp3"&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 constraint is that &lt;code&gt;audio_url&lt;/code&gt; must be a publicly accessible CDN address and support the &lt;code&gt;.mp3&lt;/code&gt; suffix. In other words, this endpoint is best used after your app has already placed the reference audio somewhere the API can fetch.&lt;/p&gt;

&lt;p&gt;The response gives you a normalized audio object. The key field is &lt;code&gt;data.audio_id&lt;/code&gt;, which is the song ID after upload. The response can also include extracted or generated context such as &lt;code&gt;lyric&lt;/code&gt;, &lt;code&gt;style&lt;/code&gt;, &lt;code&gt;image_url&lt;/code&gt;, &lt;code&gt;image_large_url&lt;/code&gt;, &lt;code&gt;audio_url&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, and &lt;code&gt;duration&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Make the upload request
&lt;/h2&gt;

&lt;p&gt;Here is the complete cURL call from the integration shape:&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/suno/upload'&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;'{
    "audio_url": "https://cdn.acedata.cloud/suno_demo.mp3"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a real application, replace the demo URL with a direct MP3 URL from your own storage or CDN. I would validate three things before calling the API:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The URL is reachable without user authentication.&lt;/li&gt;
&lt;li&gt;The file is an MP3 URL, not a web page that embeds a player.&lt;/li&gt;
&lt;li&gt;Your backend stores the original source URL before sending the request.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That third point matters because the returned data tells you what the platform produced, while the source URL tells you what your user originally provided.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Read the response as a workflow object
&lt;/h2&gt;

&lt;p&gt;A successful response has this general shape:&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;"058f8450-3df4-4f8b-8b64-ebc2e59ed3bc"&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;"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;"00135f7d-cda1-4d70-b007-779f07143586"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lyric"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[Intro]&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Ha-ha-ha-ha-ha-ha..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Upbeat bubblegum pop track with a high-energy electronic production style..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"image_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cdn2.suno.ai/image_00135f7d-cda1-4d70-b007-779f07143586.jpeg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"image_large_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://cdn2.suno.ai/image_large_00135f7d-cda1-4d70-b007-779f07143586.jpeg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"audio_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cdn1.suno.ai/00135f7d-cda1-4d70-b007-779f07143586.mp3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"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;"up-d6c3970d-6db1-41e3-b966-90539c93678a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;131.16&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 fields I would persist are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;task_id&lt;/code&gt; for tracking the upload request&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data.audio_id&lt;/code&gt; for later music generation or extension flows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data.audio_url&lt;/code&gt; for playback&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data.duration&lt;/code&gt; for UI display and validation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data.lyric&lt;/code&gt; and &lt;code&gt;data.style&lt;/code&gt; if your product lets users review or edit the extracted musical context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Treat the upload response as the “asset registration” step. After this, your app has an ID it can pass to later music-generation operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Connect it to secondary creation
&lt;/h2&gt;

&lt;p&gt;The document notes that once you have the song ID, you can use the Suno Audios Generation API to generate custom songs. The specific handoff is to pass &lt;code&gt;action&lt;/code&gt; as &lt;code&gt;upload_extend&lt;/code&gt; and use the returned &lt;code&gt;audio_id&lt;/code&gt; as the reference song ID.&lt;/p&gt;

&lt;p&gt;A clean backend abstraction 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;uploadReferenceAudio({ audio_url }) -&amp;gt; { task_id, audio_id, lyric, style, audio_url, duration }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the next workflow step can accept &lt;code&gt;audio_id&lt;/code&gt; without needing to know where the original file came from.&lt;/p&gt;

&lt;p&gt;This separation helps a lot in product code. Uploading reference audio, reviewing extracted context, and generating a derivative song are different user actions. Keeping them separate makes the interface easier to debug and easier to retry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Design the user flow around constraints
&lt;/h2&gt;

&lt;p&gt;Because the API expects a public MP3 URL, your user-facing flow should make that constraint obvious. A practical flow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User uploads an MP3 to your app.&lt;/li&gt;
&lt;li&gt;Your backend stores it on a CDN or object storage bucket.&lt;/li&gt;
&lt;li&gt;Your backend calls &lt;code&gt;POST /suno/upload&lt;/code&gt; with the public &lt;code&gt;audio_url&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Your app displays the returned title, duration, cover image, lyric, and style.&lt;/li&gt;
&lt;li&gt;The user confirms whether to continue with a secondary creation step using &lt;code&gt;audio_id&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the upload fails, keep the original user file and source URL so the user does not have to start again. If the upload succeeds but later generation fails, you still have &lt;code&gt;audio_id&lt;/code&gt; and can retry the downstream step.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small but useful primitive
&lt;/h2&gt;

&lt;p&gt;This endpoint is deliberately narrow, and that is what makes it useful. It does not ask you to model the entire music workflow in one request. It gives you one reliable bridge from an existing MP3 reference to an &lt;code&gt;audio_id&lt;/code&gt; that can be used later.&lt;/p&gt;

&lt;p&gt;For builders, that is a good boundary: one API call to register the reference, one stored ID for future work, and enough metadata to show users what was understood from their audio.&lt;/p&gt;

&lt;p&gt;The full field reference is in the Suno Upload API documentation: &lt;a href="https://platform.acedata.cloud/documents/suno-upload-integration" rel="noopener noreferrer"&gt;https://platform.acedata.cloud/documents/suno-upload-integration&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>tutorial</category>
      <category>devtools</category>
    </item>
    <item>
      <title>How to Add Flux Image Generation to Cursor with MCP</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Sun, 26 Jul 2026 01:04:53 +0000</pubDate>
      <link>https://dev.to/germey/how-to-connect-remote-mcp-tools-to-claude-code-153f</link>
      <guid>https://dev.to/germey/how-to-connect-remote-mcp-tools-to-claude-code-153f</guid>
      <description>&lt;p&gt;If you already live inside Cursor, the fastest image workflow is not another browser tab — it is asking your editor to generate or edit an image while it still has the context of the code, copy, and UI you are building.&lt;/p&gt;

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

&lt;p&gt;Flux MCP is a small but practical setup: it exposes Flux image tools to Cursor through the Model Context Protocol. Once configured, you can ask Cursor to create assets while you are working on a product surface, README, landing page, or design prototype.&lt;/p&gt;

&lt;p&gt;The source document describes two available tools:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flux_generate_image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text-to-image generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flux_edit_image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Image editing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That means you can keep common builder tasks close to your project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generate several visual directions for an empty state illustration;&lt;/li&gt;
&lt;li&gt;refine the most promising image with a higher-quality pass;&lt;/li&gt;
&lt;li&gt;edit an existing background or image without starting from scratch;&lt;/li&gt;
&lt;li&gt;batch-generate a small set of consistent avatars or UI assets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not meant to replace a full design system or a designer's review. It is most useful in the messy middle: when you need something concrete enough to unblock a UI decision, a prototype, or a draft.&lt;/p&gt;

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

&lt;p&gt;Cursor can talk to MCP servers through a local project configuration file. For Flux, the documented server is an HTTP MCP endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://flux.mcp.acedata.cloud/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Authentication is passed with a bearer token in the &lt;code&gt;Authorization&lt;/code&gt; header. The minimal Cursor configuration from the documentation 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;"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;"flux"&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://flux.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 yourToken"&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;In a Cursor project, place this in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.cursor/mcp.json
&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 API token. The same source notes that a single token can be used across MCP servers, so you do not need a separate token just for Flux.&lt;/p&gt;

&lt;p&gt;A practical security note: avoid committing &lt;code&gt;.cursor/mcp.json&lt;/code&gt; if it contains a real token. Add it to &lt;code&gt;.gitignore&lt;/code&gt;, or use a team-approved secret-management pattern if your workflow supports one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Configure Cursor
&lt;/h2&gt;

&lt;p&gt;Create the MCP config file at the root of your project:&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; .cursor
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .cursor/mcp.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;'
{
  "mcpServers": {
    "flux": {
      "type": "http",
      "url": "https://flux.mcp.acedata.cloud/mcp",
      "headers": {
        "Authorization": "Bearer yourToken"
      }
    }
  }
}
&lt;/span&gt;&lt;span class="no"&gt;JSON
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open Cursor and check that the MCP server is available in the editor's MCP/tooling area. The exact UI may change over time, but the important pieces are stable: server name &lt;code&gt;flux&lt;/code&gt;, type &lt;code&gt;http&lt;/code&gt;, URL &lt;code&gt;https://flux.mcp.acedata.cloud/mcp&lt;/code&gt;, and the &lt;code&gt;Authorization&lt;/code&gt; bearer header.&lt;/p&gt;

&lt;p&gt;For other editors, the document points to similar configuration patterns: VS Code uses &lt;code&gt;.vscode/mcp.json&lt;/code&gt; with the same configuration under &lt;code&gt;servers&lt;/code&gt;, while Windsurf uses &lt;code&gt;.windsurf/mcp.json&lt;/code&gt; under &lt;code&gt;mcpServers&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Generate quick visual directions
&lt;/h2&gt;

&lt;p&gt;The best first use case is fast iteration. Suppose you are building an app screen with an empty state and the page currently feels unfinished. Instead of leaving a gray placeholder, ask Cursor for multiple directions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use flux-dev to quickly generate 3 different styles of "no data" empty state illustrations, trying flat, 3D, and hand-drawn styles.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The documentation describes Dev mode as the fastest path for trial and error. The point is not to get the final asset immediately. The point is to compare directions while you are still thinking about the component, the surrounding copy, and the audience.&lt;/p&gt;

&lt;p&gt;Once one direction feels right, you can ask for a refinement pass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use flux-pro to refine the second image, increasing the resolution.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This two-step pattern keeps the workflow efficient: use a quick pass to find the visual idea, then spend the slower/high-quality step only on the candidate that deserves it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Edit an existing image
&lt;/h2&gt;

&lt;p&gt;Image generation is useful, but editing is often more practical in real product work. Maybe a designer sent you a background image, but the center title needs to be changed for an English prototype. The documented tool for this is &lt;code&gt;flux_edit_image&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A natural instruction 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;Edit this image, changing the Chinese title in the middle to "Welcome to the Future."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where having the tool inside Cursor helps. You can keep the task connected to the current feature branch: the asset, the component copy, the README, or the issue you are working from.&lt;/p&gt;

&lt;p&gt;Be specific when you ask for edits. Mention what should change, what must stay the same, and where the change should happen. For example: “keep the background and lighting, only replace the centered title text.” That reduces accidental regeneration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Batch small assets
&lt;/h2&gt;

&lt;p&gt;Another documented scenario is batch image generation. If your app needs default avatars for a user system, you can ask for a consistent set in one go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generate 5 minimalist geometric style avatars, each with a different color combination, circular cropped.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source document notes that the &lt;code&gt;count&lt;/code&gt; parameter can generate multiple images at once, which is useful when consistency matters. For UI work, I like to include constraints such as “same visual style,” “different color combination,” and “circular cropped” so the output is easier to drop into a mockup.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few builder tips
&lt;/h2&gt;

&lt;p&gt;Treat Flux MCP as an iteration tool, not a magic finalizer. Start broad, compare options, then refine. Keep prompts close to the product context: who the screen is for, where the asset appears, and what should not change. And when a generated image becomes part of production work, review it the same way you would review copy or code.&lt;/p&gt;

&lt;p&gt;If you want the original setup details, the Ace Data Cloud document is here: &lt;a href="https://platform.acedata.cloud/documents/cursor-mcp-flux" rel="noopener noreferrer"&gt;https://platform.acedata.cloud/documents/cursor-mcp-flux&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>tutorial</category>
      <category>api</category>
    </item>
    <item>
      <title>Integrating Claude Code with Veo MCP: Generate HD AI Videos from the Terminal</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Fri, 24 Jul 2026 01:03:18 +0000</pubDate>
      <link>https://dev.to/germey/integrating-claude-code-with-veo-mcp-generate-hd-ai-videos-from-the-terminal-1730</link>
      <guid>https://dev.to/germey/integrating-claude-code-with-veo-mcp-generate-hd-ai-videos-from-the-terminal-1730</guid>
      <description>&lt;p&gt;When I need a short product demo or a visual explanation for a README, the slow part is rarely the idea. The slow part is leaving the terminal, opening another tool, and translating a developer task into a video-editing workflow.&lt;/p&gt;

&lt;p&gt;This guide walks through a terminal-first setup: connecting Claude Code to the Veo MCP server so you can ask for short AI-generated videos in plain language while staying inside your project workspace.&lt;/p&gt;

&lt;p&gt;The source workflow comes from the Ace Data Cloud documentation for Claude Code + Veo MCP. Veo is described there as an AI video generation model from Google, with support for 1080p output and video generation with accompanying audio. In practice, the interesting part for builders is not just the model. It is the MCP layer: Claude Code can call a video tool directly from a coding session.&lt;/p&gt;

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

&lt;p&gt;After the MCP server is connected, Claude Code can use two Veo tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;veo_generate_video&lt;/code&gt;: generate video from text or images&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;veo_generate_video_with_audio&lt;/code&gt;: generate video with audio&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That covers a few practical cases that come up in engineering work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A small app demo, such as a phone screen showing weather data while the background changes from sunny to rainy&lt;/li&gt;
&lt;li&gt;A README animation, such as data moving from an input box to a result panel through three processing nodes&lt;/li&gt;
&lt;li&gt;A terminal-style video with audio, such as code being typed on a black background, keyboard clicks, a successful compile, and a green check mark&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point is not to replace careful design work. It is to make lightweight visual assets easier to prototype when you already know the scene you want.&lt;/p&gt;

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

&lt;p&gt;The MCP server URL used by this workflow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://veo.mcp.acedata.cloud/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code connects to it over HTTP, and authentication is passed with an &lt;code&gt;Authorization&lt;/code&gt; header:&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 veo &lt;span class="nt"&gt;--transport&lt;/span&gt; http https://veo.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;A small but important detail: the &lt;code&gt;-H&lt;/code&gt; flag must be uppercase. In the Claude Code command, lowercase &lt;code&gt;-h&lt;/code&gt; means help, not an HTTP header. If the connection looks like it is not receiving your token, check this first.&lt;/p&gt;

&lt;p&gt;You can get the token from the Ace Data Cloud platform after logging in. The same token is used for the MCP server connection, so Claude Code can authenticate when it calls the Veo tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose the right configuration scope
&lt;/h2&gt;

&lt;p&gt;Claude Code lets you decide where the MCP configuration should apply. If you omit &lt;code&gt;-s&lt;/code&gt;, the default scope is &lt;code&gt;local&lt;/code&gt;, which only applies to the project directory where the command runs.&lt;/p&gt;

&lt;p&gt;The documented scopes are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Command parameter&lt;/th&gt;
&lt;th&gt;Config file&lt;/th&gt;
&lt;th&gt;Applies to&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;local&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;no &lt;code&gt;-s&lt;/code&gt; or &lt;code&gt;-s local&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.claude.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;only the project directory where the command runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-s user&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.claude.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;all projects for the current user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;project&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-s project&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.mcp.json&lt;/code&gt; in the project root&lt;/td&gt;
&lt;td&gt;the current project&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a personal experiment, local scope is usually enough. For a tool you expect to use across many repositories, &lt;code&gt;-s user&lt;/code&gt; is convenient. For a team project, &lt;code&gt;-s project&lt;/code&gt; can make sense because the config lives in the project root.&lt;/p&gt;

&lt;p&gt;One warning: do not commit real tokens to a public repository. If you use project scope and share the config through git, use an environment variable placeholder such as &lt;code&gt;${ENV_VAR}&lt;/code&gt; instead of a real bearer token.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the connection
&lt;/h2&gt;

&lt;p&gt;After adding the MCP server, run:&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 are looking for the &lt;code&gt;veo&lt;/code&gt; server to show a connected status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;veo  ✓ Connected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it does not connect, I would check three things in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is the server URL exactly &lt;code&gt;https://veo.mcp.acedata.cloud/mcp&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Did you use uppercase &lt;code&gt;-H&lt;/code&gt; for the authorization header?&lt;/li&gt;
&lt;li&gt;Is the token present after &lt;code&gt;Bearer&lt;/code&gt; and not accidentally wrapped or truncated by your shell?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try a few builder-focused prompts
&lt;/h2&gt;

&lt;p&gt;Once the server is connected, you do not need to write a separate API request by hand. You can ask Claude Code to use the Veo tool from the current session.&lt;/p&gt;

&lt;p&gt;For an app demo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I am developing a weather app. Generate a demo video: the phone screen shows weather information, and the background transitions from sunny to rainy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For documentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generate an animation for the README: data flows from the input box on the left to the result panel on the right, passing through three processing nodes in the middle.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a terminal video with audio, explicitly ask for the audio-capable tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use veo_generate_video_with_audio to generate a short video with audio: code is typed automatically on a black background, keyboard clicking sounds play, compilation succeeds, and a green ✓ appears.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These prompts are intentionally concrete. In my experience, video prompts work better when they describe the subject, motion, environment, and expected ending state instead of only saying “make a cool demo.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this fits in a developer workflow
&lt;/h2&gt;

&lt;p&gt;I would use this workflow for first-pass assets: demo clips for a landing page draft, quick README visuals, internal prototypes, or short explainers for a feature branch. The value is that the generation step sits next to the code and docs you are already editing.&lt;/p&gt;

&lt;p&gt;If you want the exact setup reference, the original Ace Data Cloud doc is here: &lt;a href="https://platform.acedata.cloud/documents/claude-code-mcp-veo" rel="noopener noreferrer"&gt;https://platform.acedata.cloud/documents/claude-code-mcp-veo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>tutorial</category>
      <category>devtools</category>
    </item>
    <item>
      <title>How to Generate and Edit Images from Claude with a NanoBanana MCP Connector</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Tue, 21 Jul 2026 01:05:01 +0000</pubDate>
      <link>https://dev.to/germey/how-to-add-real-time-google-search-to-cursor-with-an-mcp-server-15f1</link>
      <guid>https://dev.to/germey/how-to-add-real-time-google-search-to-cursor-with-an-mcp-server-15f1</guid>
      <description>&lt;p&gt;You are writing in Claude, planning a blog post or product concept, and suddenly need a quick image: not a separate design session, just a usable visual artifact from the same conversation.&lt;/p&gt;

&lt;p&gt;With a NanoBanana MCP connector, Claude can call an image generation and editing tool directly, then return the resulting image link back into the chat.&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%2F0uwzfmebsxcj4ruo88ou.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%2F0uwzfmebsxcj4ruo88ou.png" alt="Ace Data Cloud NanoBanana MCP cover" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The source document describes a Claude.ai web integration for NanoBanana MCP, a remote MCP server that wraps Google Gemini 2.5 Flash Image series capabilities behind tools Claude can call.&lt;/p&gt;

&lt;p&gt;Once connected, Claude can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate images from text descriptions with &lt;code&gt;nanobanana_generate_image&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Edit images, make partial modifications, or apply style transfers with &lt;code&gt;nanobanana_edit_image&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Query a single task with &lt;code&gt;nanobanana_get_task&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Query multiple tasks in batch with &lt;code&gt;nanobanana_get_tasks_batch&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That tool split is important. Image generation often behaves like an asynchronous workflow: submit a prompt, receive a task, query the task, then display the final image URL. In a normal API integration you would write that orchestration yourself. With MCP attached to Claude, the assistant can do the submit-and-poll loop from the chat.&lt;/p&gt;

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

&lt;p&gt;Claude.ai supports custom connectors. For NanoBanana MCP, the connector is added from Claude's settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://claude.ai/customize/connectors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, choose &lt;strong&gt;Add custom connector&lt;/strong&gt; and configure these fields:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NanoBanana&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;URL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://nanobanana.mcp.acedata.cloud/mcp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The document notes that Claude.ai uses OAuth direct connection for this flow, so there is no API token to copy into Claude. On first connection, Claude redirects to Ace Data Cloud for authorization. After allowing access, Claude returns to the connector page and discovers the available tools.&lt;/p&gt;

&lt;p&gt;A practical detail from the source guide: set each tool to &lt;strong&gt;Always allow&lt;/strong&gt; if you plan to use it frequently. Image generation commonly triggers more than one tool call, such as a generation call followed by a task query. If every call requires a confirmation dialog, the workflow feels much less conversational.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with one concrete prompt
&lt;/h2&gt;

&lt;p&gt;A good first test is a single, specific English prompt. The source document uses this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use NanoBanana to generate one image: a cute corgi astronaut floating in space, soft pastel nebula background, photorealistic, 16:9.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude's job is not just to repeat the prompt. It decides which MCP tool to call, passes the prompt and aspect ratio, receives the task information, calls &lt;code&gt;nanobanana_get_task&lt;/code&gt;, and returns the final image link when the task is complete.&lt;/p&gt;

&lt;p&gt;For best results, give the model enough visual constraints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subject: &lt;code&gt;a cute corgi astronaut&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Scene: &lt;code&gt;floating in space&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Background: &lt;code&gt;soft pastel nebula background&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Style: &lt;code&gt;photorealistic&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Aspect ratio: &lt;code&gt;16:9&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes the output easier to reuse as a blog cover, social image, or design placeholder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edit an existing image in the same thread
&lt;/h2&gt;

&lt;p&gt;The connector also supports image editing through &lt;code&gt;nanobanana_edit_image&lt;/code&gt;. The source guide gives this follow-up prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Take that corgi image and replace the helmet with a giant donut.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The useful pattern here is continuity. Claude can refer to the previous image URL from the conversation and pass it into the edit workflow. Instead of manually downloading and uploading files between tools, you can keep the iteration inside the same thread.&lt;/p&gt;

&lt;p&gt;This is especially useful for small creative changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace one object while keeping the scene.&lt;/li&gt;
&lt;li&gt;Make a partial modification to a product concept.&lt;/li&gt;
&lt;li&gt;Try a different visual theme after the first generation.&lt;/li&gt;
&lt;li&gt;Turn a rough prompt into several refinements before choosing a final image.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The workflow still needs human review. Treat each result as a candidate, not an automatic final asset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate variations instead of over-editing one image
&lt;/h2&gt;

&lt;p&gt;Sometimes the better move is not to keep editing one result. The guide shows a batch-style request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generate 4 variations of that corgi astronaut, each in a different mood.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude may issue several generation calls and then use &lt;code&gt;nanobanana_get_tasks_batch&lt;/code&gt; to retrieve the results together. This is a good way to explore composition before committing to edits.&lt;/p&gt;

&lt;p&gt;A builder-friendly workflow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask for 3–4 variations with clear differences.&lt;/li&gt;
&lt;li&gt;Pick the strongest direction.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;nanobanana_edit_image&lt;/code&gt; for targeted changes.&lt;/li&gt;
&lt;li&gt;Save the final CDN image URL for your article, slide, or prototype.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The source document notes that returned images are hosted on Ace Data Cloud's CDN, so the image links can be pasted into blogs, slide decks, or design drafts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use it as part of a broader MCP workflow
&lt;/h2&gt;

&lt;p&gt;NanoBanana is most helpful when it becomes one tool in a larger assistant workflow. For example, you can ask Claude to draft a blog outline, generate a cover image, then revise the prompt until the image matches the article angle.&lt;/p&gt;

&lt;p&gt;The same connector can handle both generation and editing, while task-query tools handle result retrieval. That makes the chat feel less like a prompt box and more like a lightweight creative pipeline.&lt;/p&gt;

&lt;p&gt;A few habits help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep prompts specific and visual.&lt;/li&gt;
&lt;li&gt;Include the target aspect ratio, such as &lt;code&gt;16:9&lt;/code&gt;, when you know the destination.&lt;/li&gt;
&lt;li&gt;Use English prompts when you want more stable adherence, as suggested by the source guide.&lt;/li&gt;
&lt;li&gt;Set tool permissions intentionally instead of approving every call one by one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;This setup is useful because it removes the context switch. You stay in Claude, describe the image you need, and let the MCP connector handle generation, editing, and task retrieval.&lt;/p&gt;

&lt;p&gt;It is not a replacement for design judgment, but it is a practical way to move from idea to usable visual draft while you are already thinking through the work.&lt;/p&gt;

&lt;p&gt;Original setup reference: &lt;a href="https://platform.acedata.cloud/documents/claude-mcp-nano-banana" rel="noopener noreferrer"&gt;https://platform.acedata.cloud/documents/claude-mcp-nano-banana&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>tutorial</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Getting Started with AceKit for Coding Agents</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Mon, 20 Jul 2026 16:28:10 +0000</pubDate>
      <link>https://dev.to/germey/getting-started-with-acekit-for-coding-agents-42kb</link>
      <guid>https://dev.to/germey/getting-started-with-acekit-for-coding-agents-42kb</guid>
      <description>&lt;p&gt;Coding agents are great at editing files, but they often stop when a task needs the outside world: generating an image, searching the web, shortening a link, or calling a model-specific API.&lt;/p&gt;

&lt;p&gt;AceKit is a small installer that wires Ace Data Cloud capabilities into coding agents such as Claude Code, Codex CLI, Cursor, Gemini, and OpenCode. Instead of manually reading multiple API docs and writing one-off scripts, you install a skill toolkit once, provide a token, and let the agent follow each skill’s &lt;code&gt;SKILL.md&lt;/code&gt; instructions.&lt;/p&gt;

&lt;p&gt;This guide walks through the practical setup and then looks at what happens behind the scenes.&lt;/p&gt;

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

&lt;p&gt;After installation, your agent can load skills for tasks that normally sit outside plain code editing. The document lists examples such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generating a README cover image&lt;/li&gt;
&lt;li&gt;turning a script into a short video&lt;/li&gt;
&lt;li&gt;generating background music&lt;/li&gt;
&lt;li&gt;searching Google SERP in real time&lt;/li&gt;
&lt;li&gt;shortening a long link into a &lt;code&gt;surl.id&lt;/code&gt; URL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current skill list shown in the document includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;acedatacloud-api   ai-chat            face-transform     fish-audio
flux-image         google-search      hailuo-video       kling-video
luma-video         midjourney-image   nano-banana-image  producer-music
seedance-video     seedream-image     short-url          sora-video
suno-music         veo-video          wan-video
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That list can change over time, so use &lt;code&gt;npx acekit list&lt;/code&gt; to inspect the current set on your machine.&lt;/p&gt;

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

&lt;p&gt;AceKit is the front door. It detects supported coding agents, installs a set of agent skills, and uses one Ace Data Cloud token for authentication.&lt;/p&gt;

&lt;p&gt;The documented requirements and fields are straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 18+&lt;/li&gt;
&lt;li&gt;install command: &lt;code&gt;npx acekit&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;optional environment variable: &lt;code&gt;ACEDATACLOUD_API_TOKEN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Claude Code skill path: &lt;code&gt;~/.claude/skills&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Codex CLI skill path: &lt;code&gt;~/.agents/skills&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Cursor / Gemini / OpenCode skill path: &lt;code&gt;~/.agents/skills&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each installed skill includes a written &lt;code&gt;SKILL.md&lt;/code&gt; manual. That matters because the agent does not need to guess how a capability works. The skill can describe authentication, parameters, polling, retries, and how to return the final artifact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Prepare the token
&lt;/h2&gt;

&lt;p&gt;AceKit can prompt for a token interactively on first run. For repeatable setup, export it before installing:&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;your_token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not commit real tokens to a repository. If you want teammates to reproduce the setup, document the variable name and let each developer provide their own value locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Install the skill toolkit
&lt;/h2&gt;

&lt;p&gt;Run the installer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx acekit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A documented run on a machine with Claude Code installed looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🃏  AceKit — wiring AI into your coding agent
✓  Detected: Claude Code
📦  Installing the AceData skill toolkit via @acedatacloud/skills …
Installed 19 skills to ~/.claude/skills
✅  Done. Try it now — ask your agent:
      "generate a hero image for this README"
      "turn this script into a 30-second video"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installation, reload the agent so it can discover the new skills.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Try a small synchronous task
&lt;/h2&gt;

&lt;p&gt;Start with something deterministic, like shortening a URL. In plain language, you can ask the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use the short-url skill to shorten https://platform.acedata.cloud/services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Behind the scenes, the document shows the direct API shape:&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 https://api.acedata.cloud/shorturl &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$KEY&lt;/span&gt;&lt;span class="s2"&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;'{"content":"https://platform.acedata.cloud/services"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a useful smoke test because it is synchronous and quick. If the agent can call this skill and return a link, your token and skill installation are working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Add real-time search to agent work
&lt;/h2&gt;

&lt;p&gt;Another simple but high-value test is search. The document gives this example call:&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 https://api.acedata.cloud/serp/google &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$KEY&lt;/span&gt;&lt;span class="s2"&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;'{"query":"OpenAI Sora release date","type":"search","number":3}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In an agent workflow, the prompt can be more natural: ask it to search for a current API behavior, compare two libraries, or summarize recent documentation. The key improvement is that the agent can fetch fresh results instead of relying only on model memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Understand asynchronous tools
&lt;/h2&gt;

&lt;p&gt;Some capabilities are not instant. The document’s image example calls the Nano Banana image endpoint:&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 https://api.acedata.cloud/nano-banana/images &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$KEY&lt;/span&gt;&lt;span class="s2"&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;'{"action":"generate","model":"nano-banana",
       "prompt":"a cute cartoon banana mascot waving hello, flat vector logo, white background"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important lesson is not the mascot prompt. It is that the installed skill can handle the workflow around the API: building the request, authenticating, polling when needed, and returning the final image to the conversation.&lt;/p&gt;

&lt;p&gt;That is where agent skills become more useful than a loose collection of curl snippets. They give the agent operational instructions, not just endpoint names.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Know where skills are installed
&lt;/h2&gt;

&lt;p&gt;The document lists these installation locations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Claude Code                  ~/.claude/skills
Codex CLI                    ~/.agents/skills
Cursor / Gemini / OpenCode   ~/.agents/skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If something does not appear in your agent, check the expected directory first, then restart the agent. For multi-agent setups, remember that some tools may read from &lt;code&gt;~/.agents/skills&lt;/code&gt; while Claude Code reads from &lt;code&gt;~/.claude/skills&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional: Claude Code environment settings
&lt;/h2&gt;

&lt;p&gt;The document also shows that Ace Data Cloud can be used with Anthropic-compatible &lt;code&gt;/v1/messages&lt;/code&gt; settings for Claude Code. The local project file is &lt;code&gt;.claude/settings.local.json&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;"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;"your 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="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;Keep this file local if it contains credentials.&lt;/p&gt;

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

&lt;p&gt;AceKit is most useful when your coding agent already understands the codebase but needs safe access to external capabilities: search, media generation, link tools, or model calls. Start with a small synchronous skill, verify the install path, then try longer-running tasks where polling matters.&lt;/p&gt;

&lt;p&gt;The full setup details are in the &lt;a href="https://platform.acedata.cloud/documents/acekit-overview" rel="noopener noreferrer"&gt;AceKit Toolkit guide&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>From One Sentence to a Product Photo: A Developer Guide to E-commerce Images with gpt-image-2</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Mon, 20 Jul 2026 16:11:32 +0000</pubDate>
      <link>https://dev.to/germey/from-one-sentence-to-a-product-photo-a-developer-guide-to-e-commerce-images-with-gpt-image-2-3cfi</link>
      <guid>https://dev.to/germey/from-one-sentence-to-a-product-photo-a-developer-guide-to-e-commerce-images-with-gpt-image-2-3cfi</guid>
      <description>&lt;p&gt;Product photography is one of those tasks that looks simple until you try to do it consistently.&lt;/p&gt;

&lt;p&gt;A founder can write the copy, set up the checkout flow, and ship a small Shopify or Gumroad page in a weekend. But the moment the product page needs a clean hero image, everything slows down: camera, lighting, background, retouching, variants, and enough visual consistency that the store does not look like a collage of unrelated photos.&lt;/p&gt;

&lt;p&gt;For indie makers and small e-commerce teams, that friction matters. A product image is often the first thing a buyer evaluates, but hiring a studio or building a mini studio is not always realistic during early validation.&lt;/p&gt;

&lt;p&gt;That is where image generation can be useful: not as a replacement for every final catalog shot, but as a fast way to create polished product concepts, listing mockups, landing-page visuals, and ad creatives from plain English.&lt;/p&gt;

&lt;p&gt;This post walks through a practical example using &lt;code&gt;gpt-image-2&lt;/code&gt;: turning one sentence into a square e-commerce product photo through the Ace Data Cloud OpenAI image endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is gpt-image-2?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;gpt-image-2&lt;/code&gt; is an image generation model that can create images from text prompts. For a developer workflow, the important part is not only that it can generate images, but that it can be called from an API with predictable parameters such as &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;prompt&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;, and output format.&lt;/p&gt;

&lt;p&gt;That makes it useful when you need to build features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;product-photo generation inside an admin dashboard&lt;/li&gt;
&lt;li&gt;automatic visual variants for marketplace listings&lt;/li&gt;
&lt;li&gt;placeholder imagery before a real shoot&lt;/li&gt;
&lt;li&gt;creative testing for ads and landing pages&lt;/li&gt;
&lt;li&gt;consistent product cards for a catalog prototype&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The example below generates a clean product listing image: a matte black reusable stainless-steel water bottle on a neutral studio background.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Write the product-photo prompt
&lt;/h2&gt;

&lt;p&gt;For product imagery, avoid vague prompts like “make it beautiful.” You get better results when you specify the object, material, background, lighting, composition, and things to exclude.&lt;/p&gt;

&lt;p&gt;Here is the exact prompt used for this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A realistic square e-commerce product photo of a matte black stainless steel water bottle centered on a light gray seamless studio background. Soft softbox lighting, gentle shadow, clean product listing style, no text, no logo, no props, sharp focus.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prompt is intentionally plain English. It describes the result a photographer or designer would understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Subject:&lt;/strong&gt; matte black stainless-steel water bottle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composition:&lt;/strong&gt; centered square product photo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background:&lt;/strong&gt; light gray seamless studio backdrop&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lighting:&lt;/strong&gt; softbox lighting with a gentle shadow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commercial constraints:&lt;/strong&gt; no text, no logo, no extra props&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The negative constraints matter. If you are making catalog images, random labels, decorative props, or hands holding the product can make the output harder to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Call the API
&lt;/h2&gt;

&lt;p&gt;The request is a normal HTTP &lt;code&gt;POST&lt;/code&gt; to the image generation endpoint:&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/openai/images/generations"&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 &lt;/span&gt;&lt;span class="nv"&gt;$ACEDATACLOUD_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&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": "gpt-image-2",
    "prompt": "A realistic square e-commerce product photo of a matte black stainless steel water bottle centered on a light gray seamless studio background. Soft softbox lighting, gentle shadow, clean product listing style, no text, no logo, no props, sharp focus.",
    "size": "1024x1024",
    "quality": "medium",
    "n": 1,
    "output_format": "png",
    "response_format": "url",
    "background": "opaque"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key fields are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;model&lt;/code&gt;: set this to &lt;code&gt;gpt-image-2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prompt&lt;/code&gt;: the English product-photo description&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;size&lt;/code&gt;: &lt;code&gt;1024x1024&lt;/code&gt;, which works well for square product listings&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n&lt;/code&gt;: number of images to generate&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;response_format&lt;/code&gt;: &lt;code&gt;url&lt;/code&gt;, so the generated image can be embedded or downloaded&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;output_format&lt;/code&gt;: &lt;code&gt;png&lt;/code&gt;, useful for product and UI assets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a real application, you would usually store the returned image URL or copy the file into your own object storage. For a prototype, using the returned URL directly is often enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Show the result
&lt;/h2&gt;

&lt;p&gt;Here is the generated product image from the prompt above:&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%2F9imzbshc26l480ipt660.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%2F9imzbshc26l480ipt660.png" alt="Generated matte black water bottle product photo" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The output is not trying to be a lifestyle campaign image. It is a clean product-photo style asset: centered object, neutral background, soft shadow, and no distracting props. That is exactly the kind of image you can use while testing a product page layout or creating a first-pass catalog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Use it in a simple product workflow
&lt;/h2&gt;

&lt;p&gt;A practical workflow for an indie maker might look like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with a short product description from your catalog.&lt;/li&gt;
&lt;li&gt;Convert that description into a structured prompt.&lt;/li&gt;
&lt;li&gt;Generate one to four square product images.&lt;/li&gt;
&lt;li&gt;Pick the best image and place it into your storefront mockup.&lt;/li&gt;
&lt;li&gt;Later, replace it with a real photo shoot if the product validates.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, if your database has this product 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;"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;"Reusable Stainless Steel Bottle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"matte black"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"material"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"brushed stainless steel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"minimal outdoor lifestyle brand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"light gray studio"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can generate a prompt template 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;A realistic square e-commerce product photo of a {color} {material} {name}, centered on a {background} seamless studio background. Softbox lighting, gentle shadow, clean product listing style, no text, no logo, no extra props, sharp focus.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That template is simple enough to maintain, but structured enough to produce consistent results across a small catalog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this works well
&lt;/h2&gt;

&lt;p&gt;This approach is especially useful before you have perfect assets.&lt;/p&gt;

&lt;p&gt;If you are validating a product idea, you can generate listing images before ordering inventory. If you are building a marketplace, you can create placeholder visuals for empty categories. If you are designing a product-page builder, you can generate realistic demo content instead of relying on generic stock photos.&lt;/p&gt;

&lt;p&gt;It also helps with creative exploration. You can test different backgrounds, lighting styles, or seasonal variants without setting up a new shoot each time. For example, you might try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“on a warm beige studio background”&lt;/li&gt;
&lt;li&gt;“with dramatic side lighting and a dark charcoal backdrop”&lt;/li&gt;
&lt;li&gt;“minimal premium skincare product photography style”&lt;/li&gt;
&lt;li&gt;“clean Amazon-style white background, no props”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important part is to keep prompts operational. Describe the asset you want, the constraints it must satisfy, and how it will be used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to be careful
&lt;/h2&gt;

&lt;p&gt;Generated product images should be used responsibly. If the image represents a real SKU, make sure it does not mislead buyers about shape, texture, size, included accessories, or branding. For final product pages, you may still want real photography or carefully reviewed generated assets.&lt;/p&gt;

&lt;p&gt;A good rule is: use generation to speed up concepting, layout design, and early testing; use human review before anything becomes a customer-facing claim.&lt;/p&gt;

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

&lt;p&gt;For developers and indie makers, the value of &lt;code&gt;gpt-image-2&lt;/code&gt; is not magic. It is workflow compression. You can move from product description to usable visual asset with one API call, then iterate on the prompt until the image fits your store or prototype.&lt;/p&gt;

&lt;p&gt;If you want to try the same workflow in a browser before wiring it into code, you can experiment with the image studio here: &lt;a href="https://studio.acedata.cloud/openai-image" rel="noopener noreferrer"&gt;https://studio.acedata.cloud/openai-image&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>api</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>From One Sentence to a Product Photo: Generating E-commerce Images with gpt-image-2</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Mon, 20 Jul 2026 16:10:54 +0000</pubDate>
      <link>https://dev.to/germey/from-one-sentence-to-a-product-photo-generating-e-commerce-images-with-gpt-image-2-5d93</link>
      <guid>https://dev.to/germey/from-one-sentence-to-a-product-photo-generating-e-commerce-images-with-gpt-image-2-5d93</guid>
      <description>&lt;p&gt;Product photography is one of those tasks that looks simple until you try to do it consistently.&lt;/p&gt;

&lt;p&gt;A founder can write the copy, set up the checkout flow, and ship a small Shopify or Gumroad page in a weekend. But the moment the product page needs a clean hero image, everything slows down: you need a camera, lighting, a neutral background, retouching, variants for different angles, and enough consistency that the store does not look like a collage of unrelated photos.&lt;/p&gt;

&lt;p&gt;For indie makers and small e-commerce teams, that friction matters. A product image is often the first thing a buyer evaluates, but hiring a studio or setting up a mini studio is not always realistic during the early validation phase.&lt;/p&gt;

&lt;p&gt;That is where image generation can be useful: not as a replacement for every final catalog shot, but as a fast way to create polished product concepts, listing mockups, landing-page visuals, and ad creatives from plain English.&lt;/p&gt;

&lt;p&gt;In this post, I will walk through a practical example using &lt;code&gt;gpt-image-2&lt;/code&gt;: turning one sentence into a square e-commerce product photo through the Ace Data Cloud OpenAI image endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is gpt-image-2?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;gpt-image-2&lt;/code&gt; is an image generation model that can create images from text prompts. For a developer workflow, the important part is not only that it can generate images, but that it can be called from an API with predictable parameters such as &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;prompt&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;, and output format.&lt;/p&gt;

&lt;p&gt;That makes it useful when you need to build features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;product-photo generation inside an admin dashboard&lt;/li&gt;
&lt;li&gt;automatic visual variants for marketplace listings&lt;/li&gt;
&lt;li&gt;placeholder imagery before a real shoot&lt;/li&gt;
&lt;li&gt;creative testing for ads and landing pages&lt;/li&gt;
&lt;li&gt;consistent product cards for a catalog prototype&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The example below generates a clean product listing image: a matte black reusable stainless-steel water bottle on a neutral studio background.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Write the product-photo prompt
&lt;/h2&gt;

&lt;p&gt;For product imagery, I usually avoid vague prompts like “make it beautiful.” You get better results when you specify the object, material, background, lighting, composition, and things to exclude.&lt;/p&gt;

&lt;p&gt;Here is the exact prompt used for this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A realistic square e-commerce product photo of a matte black stainless steel water bottle centered on a light gray seamless studio background. Soft softbox lighting, gentle shadow, clean product listing style, no text, no logo, no props, sharp focus.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prompt is intentionally plain English. It describes the result a photographer or designer would understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Subject:&lt;/strong&gt; matte black stainless-steel water bottle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composition:&lt;/strong&gt; centered square product photo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background:&lt;/strong&gt; light gray seamless studio backdrop&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lighting:&lt;/strong&gt; softbox lighting with a gentle shadow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commercial constraints:&lt;/strong&gt; no text, no logo, no extra props&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The negative constraints matter. If you are making catalog images, random labels, decorative props, or hands holding the product can make the output harder to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Call the API
&lt;/h2&gt;

&lt;p&gt;The request is a normal HTTP &lt;code&gt;POST&lt;/code&gt; to the image generation endpoint:&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/openai/images/generations"&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 &lt;/span&gt;&lt;span class="nv"&gt;$ACEDATACLOUD_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&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": "gpt-image-2",
    "prompt": "A realistic square e-commerce product photo of a matte black stainless steel water bottle centered on a light gray seamless studio background. Soft softbox lighting, gentle shadow, clean product listing style, no text, no logo, no props, sharp focus.",
    "size": "1024x1024",
    "quality": "medium",
    "n": 1,
    "output_format": "png",
    "response_format": "url",
    "background": "opaque"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key fields are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;model&lt;/code&gt;: set this to &lt;code&gt;gpt-image-2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prompt&lt;/code&gt;: the English product-photo description&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;size&lt;/code&gt;: &lt;code&gt;1024x1024&lt;/code&gt;, which works well for square product listings&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n&lt;/code&gt;: number of images to generate&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;response_format&lt;/code&gt;: &lt;code&gt;url&lt;/code&gt;, so the generated image can be embedded or downloaded&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;output_format&lt;/code&gt;: &lt;code&gt;png&lt;/code&gt;, useful for product and UI assets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a real application, you would usually store the returned image URL or copy the file into your own object storage. For a prototype, using the returned URL directly is often enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Show the result
&lt;/h2&gt;

&lt;p&gt;Here is the generated product image from the prompt above:&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%2F9imzbshc26l480ipt660.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%2F9imzbshc26l480ipt660.png" alt="Generated matte black water bottle product photo" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The output is not trying to be a lifestyle campaign image. It is a clean product-photo style asset: centered object, neutral background, soft shadow, and no distracting props. That is exactly the kind of image you can use while testing a product page layout or creating a first-pass catalog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Use it in a simple product workflow
&lt;/h2&gt;

&lt;p&gt;A practical workflow for an indie maker might look like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with a short product description from your catalog.&lt;/li&gt;
&lt;li&gt;Convert that description into a structured prompt.&lt;/li&gt;
&lt;li&gt;Generate one to four square product images.&lt;/li&gt;
&lt;li&gt;Pick the best image and place it into your storefront mockup.&lt;/li&gt;
&lt;li&gt;Later, replace it with a real photo shoot if the product validates.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, if your database has this product 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;"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;"Reusable Stainless Steel Bottle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"matte black"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"material"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"brushed stainless steel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"minimal outdoor lifestyle brand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"light gray studio"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can generate a prompt template 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;A realistic square e-commerce product photo of a {color} {material} {name}, centered on a {background} seamless studio background. Softbox lighting, gentle shadow, clean product listing style, no text, no logo, no extra props, sharp focus.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That template is simple enough to maintain, but structured enough to produce consistent results across a small catalog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this works well
&lt;/h2&gt;

&lt;p&gt;This approach is especially useful before you have perfect assets.&lt;/p&gt;

&lt;p&gt;If you are validating a product idea, you can generate listing images before ordering inventory. If you are building a marketplace, you can create placeholder visuals for empty categories. If you are designing a product-page builder, you can generate realistic demo content instead of relying on generic stock photos.&lt;/p&gt;

&lt;p&gt;It also helps with creative exploration. You can test different backgrounds, lighting styles, or seasonal variants without setting up a new shoot each time. For example, you might try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“on a warm beige studio background”&lt;/li&gt;
&lt;li&gt;“with dramatic side lighting and a dark charcoal backdrop”&lt;/li&gt;
&lt;li&gt;“minimal premium skincare product photography style”&lt;/li&gt;
&lt;li&gt;“clean Amazon-style white background, no props”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important part is to keep prompts operational. Describe the asset you want, the constraints it must satisfy, and how it will be used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to be careful
&lt;/h2&gt;

&lt;p&gt;Generated product images should be used responsibly. If the image represents a real SKU, make sure it does not mislead buyers about shape, texture, size, included accessories, or branding. For final product pages, you may still want real photography or carefully reviewed generated assets.&lt;/p&gt;

&lt;p&gt;A good rule is: use generation to speed up concepting, layout design, and early testing; use human review before anything becomes a customer-facing claim.&lt;/p&gt;

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

&lt;p&gt;For developers and indie makers, the value of &lt;code&gt;gpt-image-2&lt;/code&gt; is not magic. It is workflow compression. You can move from product description to usable visual asset with one API call, then iterate on the prompt until the image fits your store or prototype.&lt;/p&gt;

&lt;p&gt;If you want to try the same workflow in a browser before wiring it into code, you can experiment with the image studio here: &lt;a href="https://studio.acedata.cloud/openai-image" rel="noopener noreferrer"&gt;https://studio.acedata.cloud/openai-image&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>api</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>How to Add Generate and Edit Image Workflows with the Nano Banana Images API</title>
      <dc:creator>Germey</dc:creator>
      <pubDate>Mon, 20 Jul 2026 01:07:31 +0000</pubDate>
      <link>https://dev.to/germey/how-to-add-generate-and-edit-image-workflows-with-the-nano-banana-images-api-42dc</link>
      <guid>https://dev.to/germey/how-to-add-generate-and-edit-image-workflows-with-the-nano-banana-images-api-42dc</guid>
      <description>&lt;p&gt;A lot of image features start simple: generate one picture from a prompt, or edit an existing image from a product page. The hard part is turning that into a backend workflow that is predictable enough for an app.&lt;/p&gt;

&lt;p&gt;This guide shows how to wire a small generate-and-edit pipeline with the Ace Data Cloud Nano Banana Images API. We will keep it practical: one endpoint, the real request fields, a curl example, a Python wrapper, and a few notes that matter when you move from playground experiments to application code.&lt;/p&gt;

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

&lt;p&gt;The Nano Banana Images API supports two actions through the same endpoint:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;generate&lt;/code&gt;: create images from a text prompt&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;edit&lt;/code&gt;: edit one or more input images with a prompt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The documented interface 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 /nano-banana/images&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Auth header: &lt;code&gt;authorization: Bearer {token}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Request headers: &lt;code&gt;accept: application/json&lt;/code&gt; and &lt;code&gt;content-type: application/json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Optional &lt;code&gt;callback_url&lt;/code&gt; for asynchronous completion notifications&lt;/li&gt;
&lt;li&gt;Optional &lt;code&gt;count&lt;/code&gt; from 1 to 4, defaulting to 1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model field is optional. The documented choices include &lt;code&gt;nano-banana&lt;/code&gt;, &lt;code&gt;nano-banana-2-lite&lt;/code&gt;, &lt;code&gt;nano-banana-2&lt;/code&gt;, &lt;code&gt;nano-banana-pro&lt;/code&gt;, plus corresponding &lt;code&gt;:official&lt;/code&gt; variants such as &lt;code&gt;nano-banana-pro:official&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For app developers, the useful part is that generation and editing share the same response idea: a successful call returns &lt;code&gt;success&lt;/code&gt;, a &lt;code&gt;task_id&lt;/code&gt;, a &lt;code&gt;trace_id&lt;/code&gt;, and a &lt;code&gt;data[]&lt;/code&gt; list containing image results.&lt;/p&gt;

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

&lt;p&gt;For generation, the minimum required fields are &lt;code&gt;action&lt;/code&gt; and &lt;code&gt;prompt&lt;/code&gt;. You can add &lt;code&gt;model&lt;/code&gt; when you want a specific model, and &lt;code&gt;count&lt;/code&gt; when you want more than one result.&lt;/p&gt;

&lt;p&gt;Here is the documented cURL pattern:&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/nano-banana/images'&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;'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;'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": "nano-banana-pro",
    "prompt": "A photorealistic close-up portrait of an elderly Japanese ceramicist with deep, sun-etched wrinkles and a warm, knowing smile. He is carefully inspecting a freshly glazed tea bowl. The setting is his rustic, sun-drenched workshop. The scene is illuminated by soft, golden hour light streaming through a window, highlighting the fine texture of the clay. Captured with an 85mm portrait lens, resulting in a soft, blurred background (bokeh). The overall mood is serene and masterful. Vertical portrait orientation.",
    "count": 1
  }'&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;"70e6931b-6e34-43db-9e36-8765e2809d04"&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;"60df8d38-f265-4986-aec7-75c9220bced2"&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;"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;"A photorealistic close-up portrait of an elderly Japanese ceramicist..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"image_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://platform2.cdn.acedata.cloud/nanobanana/1d0160b4-93f9-4229-8926-ea9ef0bed336.png"&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;In a production backend, &lt;code&gt;image_url&lt;/code&gt; is the field you usually persist. &lt;code&gt;task_id&lt;/code&gt; is useful for correlating the request with your own job record, and &lt;code&gt;trace_id&lt;/code&gt; is useful when debugging failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate: keep prompts specific and save the result URL
&lt;/h2&gt;

&lt;p&gt;A generation request is straightforward, but prompt quality still matters. I prefer to store prompts as structured templates instead of raw text scattered through the codebase. For example, a product image workflow could build the prompt from fields like subject, style, lighting, and orientation.&lt;/p&gt;

&lt;p&gt;Here is a compact Python wrapper:&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;API_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/nano-banana/images&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_image&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="n"&gt;prompt&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;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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-pro&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&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;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;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;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;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;action&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;generate&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;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;model&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&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="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;count&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;API_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="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120&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;result&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&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="p"&gt;[])]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API supports &lt;code&gt;count&lt;/code&gt; from 1 to 4. The document notes that if some images fail, only successful images are returned and billed. That means your code should treat &lt;code&gt;data&lt;/code&gt; as a list that may contain fewer items than requested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edit: pass one or more source images with &lt;code&gt;image_urls&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;For editing, set &lt;code&gt;action&lt;/code&gt; to &lt;code&gt;edit&lt;/code&gt;, provide a &lt;code&gt;prompt&lt;/code&gt;, and pass source images through &lt;code&gt;image_urls&lt;/code&gt;. The document says these can be publicly accessible &lt;code&gt;http&lt;/code&gt; or &lt;code&gt;https&lt;/code&gt; URLs, or base64-encoded images such as a &lt;code&gt;data:image/png;base64,...&lt;/code&gt; string.&lt;/p&gt;

&lt;p&gt;A documented edit request 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="s1"&gt;'https://api.acedata.cloud/nano-banana/images'&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;'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;'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": "edit",
    "prompt": "let this man wear on this T-shirt",
    "image_urls": [
      "https://cdn.acedata.cloud/v8073y.png",
      "https://cdn.acedata.cloud/44xlah.png"
    ],
    "count": 1
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That pattern is useful for common app features: virtual try-on experiments, combining a product image with a scene, changing an object style, or applying a brand asset to a generated background. The key is to make the relationship between the images explicit in the prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add callbacks when the UI should not wait
&lt;/h2&gt;

&lt;p&gt;The API supports an optional &lt;code&gt;callback_url&lt;/code&gt; so your service can receive task completion notifications and results asynchronously. This is usually a better fit for web apps than keeping a browser request open.&lt;/p&gt;

&lt;p&gt;A simple architecture is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your frontend submits a generation or edit job to your backend.&lt;/li&gt;
&lt;li&gt;Your backend calls &lt;code&gt;POST /nano-banana/images&lt;/code&gt; with &lt;code&gt;callback_url&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You store &lt;code&gt;task_id&lt;/code&gt; and initial status in your database.&lt;/li&gt;
&lt;li&gt;When the callback arrives, you save the returned &lt;code&gt;image_url&lt;/code&gt; values and notify the user interface.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Even if you start synchronously, design your database around jobs rather than one-off responses. Image workflows often become queues later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle errors as first-class data
&lt;/h2&gt;

&lt;p&gt;The documented error response includes an &lt;code&gt;error&lt;/code&gt; object and a &lt;code&gt;trace_id&lt;/code&gt;. Do not throw that information away. Log the trace ID with your internal request ID so you can debug problems later.&lt;/p&gt;

&lt;p&gt;For example, wrap your request like this:&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;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_image&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="n"&gt;prompt&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;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&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;exc&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&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;Nano Banana request failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;body&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="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a real system, you would parse the JSON body and store &lt;code&gt;error.code&lt;/code&gt;, &lt;code&gt;error.message&lt;/code&gt;, and &lt;code&gt;trace_id&lt;/code&gt; in your job table.&lt;/p&gt;

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

&lt;p&gt;The nice thing about this API shape is that it keeps the workflow small: one endpoint, an &lt;code&gt;action&lt;/code&gt; switch, a prompt, optional source images, and a result list of image URLs. Start with &lt;code&gt;generate&lt;/code&gt; for prompt-only features, move to &lt;code&gt;edit&lt;/code&gt; when you need source images, and add &lt;code&gt;callback_url&lt;/code&gt; once the user experience needs background jobs.&lt;/p&gt;

&lt;p&gt;The full Ace Data Cloud document has the complete examples and parameter notes: &lt;a href="https://platform.acedata.cloud/documents/nano-banana-images-integration" rel="noopener noreferrer"&gt;Nano Banana Images API Integration Guide&lt;/a&gt;.&lt;/p&gt;

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