<?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: vancine-fan</title>
    <description>The latest articles on DEV Community by vancine-fan (@vancine-fan).</description>
    <link>https://dev.to/vancine-fan</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%2F4033382%2Fbbc76161-37bc-41e8-bc1e-015b14057418.png</url>
      <title>DEV Community: vancine-fan</title>
      <link>https://dev.to/vancine-fan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vancine-fan"/>
    <language>en</language>
    <item>
      <title>Kimi K3 in OpenCode: What One Finished Coding Task Actually Cost</title>
      <dc:creator>vancine-fan</dc:creator>
      <pubDate>Tue, 21 Jul 2026 00:36:29 +0000</pubDate>
      <link>https://dev.to/vancine-fan/kimi-k3-in-opencode-what-one-finished-coding-task-actually-cost-38m1</link>
      <guid>https://dev.to/vancine-fan/kimi-k3-in-opencode-what-one-finished-coding-task-actually-cost-38m1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Update — July 23, 2026: I published a fuller reproducible guide covering the Starter, OpenCode configuration, evidence JSON, measured run, and its limitations:&lt;br&gt;
&lt;a href="https://xingod.me/journal/kimi-k3-opencode-starter?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=kimi_k3_opencode_starter&amp;amp;utm_content=expanded_guide" rel="noopener noreferrer"&gt;Kimi K3 in OpenCode: A Reproducible Starter and One Measured Agent Run&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Kimi K3 launched with impressive benchmark results—and a lot of debate about its price.&lt;/p&gt;

&lt;p&gt;For developers using coding agents, however, price per million tokens is only part of the story.&lt;/p&gt;

&lt;p&gt;A more useful question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How much does it cost to finish an actual coding task—with file reads, edits, tool calls, and passing tests?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We ran one controlled Kimi K3 coding task through OpenCode and recorded the result.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I work on Vancine, a third-party API aggregation platform. Vancine is not Moonshot AI and is not an official Kimi service. The cost below is measured Vancine usage for one specific run, not official Kimi pricing or a fixed-price promise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The task
&lt;/h2&gt;

&lt;p&gt;The agent received a small repository containing a faulty leap-year implementation.&lt;/p&gt;

&lt;p&gt;It had to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inspect the source code and failing tests;&lt;/li&gt;
&lt;li&gt;identify the bug;&lt;/li&gt;
&lt;li&gt;edit the source file;&lt;/li&gt;
&lt;li&gt;leave the test file unchanged;&lt;/li&gt;
&lt;li&gt;run the actual test suite;&lt;/li&gt;
&lt;li&gt;finish with all tests passing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The run used &lt;strong&gt;OpenCode v1.18.3&lt;/strong&gt; inside an isolated Docker Linux ARM64 environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Completed model steps&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Completed tool calls&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File reads&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source edits&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shell commands&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failed tool calls&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total tokens&lt;/td&gt;
&lt;td&gt;28,707&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duration&lt;/td&gt;
&lt;td&gt;84.3 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tests passed&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Measured Vancine usage&lt;/td&gt;
&lt;td&gt;$0.19&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 28,707 total tokens included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3,746 input tokens;&lt;/li&gt;
&lt;li&gt;1,019 output tokens;&lt;/li&gt;
&lt;li&gt;902 reasoning tokens;&lt;/li&gt;
&lt;li&gt;23,040 cached-read tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The run completed without recorded HTTP 429 responses, HTTP 5xx responses, provider errors, or permission failures.&lt;/p&gt;

&lt;p&gt;You can inspect the sanitized machine-readable evidence here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/VancineAI/kimi-k3-api-starter/blob/main/results/opencode-agent.verified.json" rel="noopener noreferrer"&gt;Kimi K3 OpenCode verification evidence&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What this result proves
&lt;/h2&gt;

&lt;p&gt;For this specific task, Kimi K3 successfully:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inspected multiple files;&lt;/li&gt;
&lt;li&gt;understood the failing behavior;&lt;/li&gt;
&lt;li&gt;made the required source change;&lt;/li&gt;
&lt;li&gt;invoked the test command;&lt;/li&gt;
&lt;li&gt;reached a passing solution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The source file was modified, the test file remained unchanged, and no unexpected files were created.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does not prove
&lt;/h2&gt;

&lt;p&gt;This is one controlled task—not a general benchmark.&lt;/p&gt;

&lt;p&gt;It does not prove that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every Kimi K3 task will cost $0.19;&lt;/li&gt;
&lt;li&gt;every repository will complete in six steps;&lt;/li&gt;
&lt;li&gt;Kimi K3 is universally better than Claude, GPT, GLM, or other models;&lt;/li&gt;
&lt;li&gt;every OpenCode version or coding-agent client behaves identically;&lt;/li&gt;
&lt;li&gt;$1 of starting credit will complete a particular agent task.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cline and Roo Code configuration examples are included in the starter repository, but they have not yet been independently live-verified. Only the recorded OpenCode v1.18.3 task is verified.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why cost per finished task matters
&lt;/h2&gt;

&lt;p&gt;Coding agents rarely make one isolated API request.&lt;/p&gt;

&lt;p&gt;A normal agent loop may:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;read multiple files;&lt;/li&gt;
&lt;li&gt;build and rebuild context;&lt;/li&gt;
&lt;li&gt;reason about the change;&lt;/li&gt;
&lt;li&gt;call tools;&lt;/li&gt;
&lt;li&gt;edit code;&lt;/li&gt;
&lt;li&gt;run tests;&lt;/li&gt;
&lt;li&gt;inspect failures;&lt;/li&gt;
&lt;li&gt;retry until the task is complete.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because of that, the cheapest model per token is not necessarily the cheapest model per finished task.&lt;/p&gt;

&lt;p&gt;A useful coding-model evaluation should report:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whether the task was completed;&lt;/li&gt;
&lt;li&gt;whether the tests passed;&lt;/li&gt;
&lt;li&gt;model steps;&lt;/li&gt;
&lt;li&gt;successful and failed tool calls;&lt;/li&gt;
&lt;li&gt;total tokens;&lt;/li&gt;
&lt;li&gt;elapsed time;&lt;/li&gt;
&lt;li&gt;final billed usage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One task is not a leaderboard, but it is a more concrete starting point than benchmark scores or token prices alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Kimi K3 with OpenCode
&lt;/h2&gt;

&lt;p&gt;Vancine exposes the model alias &lt;code&gt;kimi-k3&lt;/code&gt; through an OpenAI-compatible API.&lt;/p&gt;

&lt;p&gt;A minimal OpenCode provider configuration 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;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://opencode.ai/config.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"vancine"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"npm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@ai-sdk/openai-compatible"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Vancine"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"baseURL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://vancine.com/v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"apiKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{env:VANCINE_API_KEY}"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"models"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"kimi-k3"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Kimi K3"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the API key in an environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;VANCINE_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-api-key"&lt;/span&gt;
opencode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then select:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vancine/kimi-k3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not commit real API keys to source control or paste them into shared configuration files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Direct API request
&lt;/h2&gt;

&lt;p&gt;The same model can be called through the OpenAI-compatible Chat Completions 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 https://vancine.com/v1/chat/completions &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;$VANCINE_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": "kimi-k3",
    "messages": [
      {
        "role": "user",
        "content": "Review this function and identify the bug."
      }
    ]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Vancine model alias is &lt;code&gt;kimi-k3&lt;/code&gt;. Kimi’s first-party documentation uses &lt;code&gt;k3&lt;/code&gt; for the official Kimi Code model ID, so always use the identifier required by the provider you are connecting to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce the setup
&lt;/h2&gt;

&lt;p&gt;The public starter repository includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenCode configuration;&lt;/li&gt;
&lt;li&gt;Cline and Roo Code configuration examples;&lt;/li&gt;
&lt;li&gt;cURL, Python, and Node.js requests;&lt;/li&gt;
&lt;li&gt;offline configuration validation;&lt;/li&gt;
&lt;li&gt;sanitized OpenCode evidence;&lt;/li&gt;
&lt;li&gt;credential-safety guidance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/VancineAI/kimi-k3-api-starter" rel="noopener noreferrer"&gt;View the Kimi K3 API Starter on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also open the complete quick start here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vancine.com/kimi-k3-api?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=kimi_k3_cost_to_solution&amp;amp;utm_content=opencode_case_study" rel="noopener noreferrer"&gt;Kimi K3 API for coding agents&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;New Vancine accounts currently receive &lt;strong&gt;$1 in starting credit&lt;/strong&gt;, with no credit card required.&lt;/p&gt;

&lt;p&gt;Usage depends on the model, prompt size, context, tool loop, and number of requests. The starting credit does not guarantee that any particular coding task will complete within that amount.&lt;/p&gt;

&lt;p&gt;The broader takeaway is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For coding agents, measure the cost of reaching a tested solution—not just the price of generating a token.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>opencode</category>
      <category>kimik3</category>
    </item>
    <item>
      <title>Seedance + n8n: Build a Bounded Async Polling Workflow</title>
      <dc:creator>vancine-fan</dc:creator>
      <pubDate>Fri, 17 Jul 2026 17:43:52 +0000</pubDate>
      <link>https://dev.to/vancine-fan/seedance-n8n-build-a-bounded-async-polling-workflow-374d</link>
      <guid>https://dev.to/vancine-fan/seedance-n8n-build-a-bounded-async-polling-workflow-374d</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article was originally published on &lt;a href="https://xingod.me/journal/seedance-n8n-bounded-async-polling" rel="noopener noreferrer"&gt;XINGOD.ME&lt;/a&gt;. This DEV edition keeps the implementation complete while adapting the explanation for automation builders.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Video generation APIs are asynchronous by design. The first request does not return a finished MP4. It returns a task identifier that you must preserve while the provider queues, processes, and eventually completes—or fails—the job.&lt;/p&gt;

&lt;p&gt;That changes the automation problem. A production-ready workflow must do more than “poll until done.” It must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;submit exactly once;&lt;/li&gt;
&lt;li&gt;preserve &lt;code&gt;task_id&lt;/code&gt; and loop configuration across every node;&lt;/li&gt;
&lt;li&gt;wait between status requests;&lt;/li&gt;
&lt;li&gt;stop immediately on success or failure;&lt;/li&gt;
&lt;li&gt;enforce a hard polling limit; and&lt;/li&gt;
&lt;li&gt;make the final timeout observable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this tutorial, the default polling budget is &lt;strong&gt;5 seconds × 120 polls&lt;/strong&gt;, or roughly &lt;strong&gt;10 minutes&lt;/strong&gt;. Both values are configurable, but the loop always remains bounded.&lt;/p&gt;

&lt;h2&gt;
  
  
  The state machine
&lt;/h2&gt;

&lt;p&gt;At a high level, the 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;submit
  → save task_id and loop state
  → wait
  → poll
  → merge the API response with carried state
  → completed / failed / timeout / continue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tested n8n workflow uses these concrete nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;manualTrigger
  → setFields
  → submitTask
  → saveTaskId
  → waitInterval
       ├─→ pollTask ─────┐
       └─→ carryState ───┴─→ Merge (append)
                               → mergeState
                               → ifCompleted
                                  ├─ true  → outputResult
                                  └─ false → ifFailed
                                               ├─ true  → failNode
                                               └─ false → ifTimeout
                                                            ├─ true  → timeoutFail
                                                            └─ false → continuePolling
                                                                          → waitInterval
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The branching around &lt;code&gt;pollTask&lt;/code&gt; and &lt;code&gt;carryState&lt;/code&gt; is the critical part. An n8n HTTP Request node replaces the current item with the HTTP response. Without a parallel state branch, the response can erase the loop counter and configuration needed for the next iteration.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Configure the request and polling budget
&lt;/h2&gt;

&lt;p&gt;Start with an Edit Fields node named &lt;code&gt;setFields&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model = Doubao-Seedance-1.5-pro
prompt = A timelapse of a flower blooming at sunrise
size = 1280x720
base_url = https://vancine.com
intervalSeconds = 5
maxPolls = 120
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;intervalSeconds&lt;/code&gt; and &lt;code&gt;maxPolls&lt;/code&gt; are configuration, not hidden constants. A different workflow may need a shorter interval or a different deadline, but it should still have an explicit upper bound.&lt;/p&gt;

&lt;p&gt;Store authentication in n8n Credentials, for example with a reusable Header Auth credential. Never paste a real API key into exported workflow JSON, screenshots, or code examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Submit once and normalize &lt;code&gt;task_id&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Configure &lt;code&gt;submitTask&lt;/code&gt; as an HTTP Request node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://vancine.com/v1/video/generations
Content-Type: application/json
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Send the configured model, prompt, and size in the JSON body. The exact supported models and request fields can change, so check the live documentation before using the workflow in production.&lt;/p&gt;

&lt;p&gt;Immediately after submission, &lt;code&gt;saveTaskId&lt;/code&gt; creates the loop envelope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;task_id = {{ $json.task_id || $json.id || ($json.data &amp;amp;&amp;amp; $json.data.task_id) || ($json.data &amp;amp;&amp;amp; $json.data.id) }}
base_url = {{ $('setFields').first().json.base_url || 'https://vancine.com' }}
intervalSeconds = {{ $('setFields').first().json.intervalSeconds || 5 }}
maxPolls = {{ $('setFields').first().json.maxPolls || 120 }}
poll_index = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The submit response is a receipt. &lt;code&gt;task_id&lt;/code&gt; is the durable hand-off value for every later status request, error message, and audit record.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Wait, then fork response and state
&lt;/h2&gt;

&lt;p&gt;Connect &lt;code&gt;saveTaskId&lt;/code&gt; to a Wait node named &lt;code&gt;waitInterval&lt;/code&gt;. Its amount is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{{ $json.intervalSeconds || 5 }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the wait, send the same item to two nodes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;pollTask&lt;/code&gt; performs the status request.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;carryState&lt;/code&gt; retains the state required by the next iteration.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;pollTask&lt;/code&gt; calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET {{$json.base_url}}/v1/video/generations/{{$json.task_id}}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;carryState&lt;/code&gt; outputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;task_id = {{ $json.task_id }}
poll_index = {{ $json.poll_index || 0 }}
maxPolls = {{ $json.maxPolls || 120 }}
intervalSeconds = {{ $json.intervalSeconds || 5 }}
base_url = {{ $json.base_url || 'https://vancine.com' }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both branches feed a Merge node configured in &lt;strong&gt;append&lt;/strong&gt; mode. The resulting item list contains the API response and the carried loop state.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Merge locally and increment exactly once
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;mergeState&lt;/code&gt; Code node makes &lt;strong&gt;no network request&lt;/strong&gt;. It identifies the two appended items, merges them, and increments the counter from the carried item exactly once:&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;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;carried&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;poll_index&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pollIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;carried&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;poll_index&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taskId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task_id&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&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;task_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&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;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="nx"&gt;carried&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task_id&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;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&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;status&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;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&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;metadata&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="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&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;error&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;resultUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;result_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;video_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="dl"&gt;''&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;metadata&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;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;poll_index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pollIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;maxPolls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;carried&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxPolls&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="na"&gt;intervalSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;carried&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intervalSeconds&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;base_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;carried&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;base_url&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://vancine.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;result_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resultUrl&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="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important invariant is:&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;pollIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;carried&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;poll_index&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The counter comes from the latest carried state, not from the initial state node. This is what allows the workflow to reach &lt;code&gt;maxPolls&lt;/code&gt; and guarantees that a non-terminal task cannot loop forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Route completed, failed, and timeout separately
&lt;/h2&gt;

&lt;p&gt;Run the terminal checks in order.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ifCompleted&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Treat &lt;code&gt;completed&lt;/code&gt; and the legacy value &lt;code&gt;SUCCESS&lt;/code&gt; as success:&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="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$json&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;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;$json&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;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUCCESS&lt;/span&gt;&lt;span class="dl"&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 true branch goes to &lt;code&gt;outputResult&lt;/code&gt;, where you can return or persist &lt;code&gt;result_url&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ifFailed&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The false branch from &lt;code&gt;ifCompleted&lt;/code&gt; reaches &lt;code&gt;ifFailed&lt;/code&gt;. Treat &lt;code&gt;failed&lt;/code&gt; and &lt;code&gt;FAILURE&lt;/code&gt; as terminal errors:&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="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FAILURE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$json&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;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;$json&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;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FAILURE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Send the true branch to a Stop And Error node. A failed generation is a business terminal state, not a reason to keep polling.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ifTimeout&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Only a non-terminal, non-failed item reaches &lt;code&gt;ifTimeout&lt;/code&gt;:&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;poll_index&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxPolls&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The true branch stops with a timeout error. The false branch reaches &lt;code&gt;continuePolling&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;continuePolling&lt;/code&gt; copies the &lt;strong&gt;latest&lt;/strong&gt; values back into the loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;task_id
poll_index
maxPolls
intervalSeconds
base_url
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It then reconnects to &lt;code&gt;waitInterval&lt;/code&gt;. With the defaults, the 120th non-terminal response enters the timeout branch instead of starting iteration 121.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Keep transport retries separate from task polling
&lt;/h2&gt;

&lt;p&gt;A task that is still &lt;code&gt;processing&lt;/code&gt; is not the same as an HTTP request that received a transient &lt;code&gt;429&lt;/code&gt; or &lt;code&gt;5xx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Use two separate budgets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a small, explicit retry policy with backoff for transient transport failures;&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;poll_index / maxPolls&lt;/code&gt; budget for valid task-status responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation prevents network noise from silently changing the business timeout. Also test the following paths independently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;completed;&lt;/li&gt;
&lt;li&gt;failed;&lt;/li&gt;
&lt;li&gt;timeout;&lt;/li&gt;
&lt;li&gt;missing &lt;code&gt;task_id&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;429&lt;/code&gt; and &lt;code&gt;5xx&lt;/code&gt;; and&lt;/li&gt;
&lt;li&gt;malformed or unknown status values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Log &lt;code&gt;task_id&lt;/code&gt;, &lt;code&gt;poll_index&lt;/code&gt;, the last status, and elapsed time. Never log the Authorization header.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the tested workflow
&lt;/h2&gt;

&lt;p&gt;The public &lt;a href="https://github.com/VancineAI/seedance-api-starter?utm_source=dev_community&amp;amp;utm_medium=organic_content&amp;amp;utm_campaign=seedance_n8n_bounded_polling&amp;amp;utm_content=starter_kit" rel="noopener noreferrer"&gt;Seedance API Starter Kit&lt;/a&gt; includes the tested n8n workflow plus Node.js, Python, cURL, and Postman examples.&lt;/p&gt;

&lt;p&gt;Additional resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vancine.com/docs?utm_source=dev_community&amp;amp;utm_medium=organic_content&amp;amp;utm_campaign=seedance_n8n_bounded_polling&amp;amp;utm_content=docs#video" rel="noopener noreferrer"&gt;Live API documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postman.com/vancine-ai/vancine-seedance-api/collection/jej2ty/vancine-seedance?utm_source=dev_community&amp;amp;utm_medium=organic_content&amp;amp;utm_campaign=seedance_n8n_bounded_polling&amp;amp;utm_content=postman" rel="noopener noreferrer"&gt;Postman Collection&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start with Vancine
&lt;/h2&gt;

&lt;p&gt;New Vancine accounts receive &lt;strong&gt;$1 in free credit&lt;/strong&gt;, with &lt;strong&gt;no credit card required&lt;/strong&gt;. You can &lt;a href="https://vancine.com/register?utm_source=dev_community&amp;amp;utm_medium=organic_content&amp;amp;utm_campaign=seedance_n8n_bounded_polling&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;create an account here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Credit usage depends on the selected model, inputs, duration, and current pricing. The $1 credit does &lt;strong&gt;not&lt;/strong&gt; guarantee that any specific video generation will complete within that amount.&lt;/p&gt;

&lt;p&gt;Bounded polling is a small architectural choice with a large operational payoff: every execution has an inspectable state, every terminal outcome has an explicit branch, and every loop has a known failure budget.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automation</category>
      <category>api</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
