Guides
How to Poll an Async Music Generation API Without Webhooks
- Written by
- Sonilo Team
- Published

Poll the status GET on a bounded schedule. Never let a failed status read create a second generation request.
Store the returned task ID, poll the documented status endpoint on a bounded schedule, and never turn a failed status read into a second generation request. For Sonilo, poll GET /v1/tasks/{task_id} every two to five seconds, respect Retry-After, stop on a documented terminal state, and validate the returned media before your application marks the job complete.
By Sonilo Team · Facts verified August 9, 2026
Sonilo publishes this guide and operates the API used in the examples. The current Sonilo OpenAPI document exposes async task creation and retrieval but no webhook-registration path or callback URL field. That makes polling the supported completion mechanism today. Treat this as a dated interface finding, not a promise that the API will never add webhooks.
Separate job creation from status retrieval
The most important design rule is simple: creating work and checking work are different operations.
A generation POST can consume credits and create a new task. A status GET only reads the existing task. RFC 9110 classifies GET as safe and idempotent, while POST is not idempotent by default. If the connection drops after a generation POST, do not assume nothing happened and blindly submit it again. First reconcile the application request with the task ID or route the ambiguous request for review.
Once you have a task ID, the Sonilo Retrieve Task reference defines the exact read path: GET /v1/tasks/{task_id}. The current OpenAPI task schema documents processing, succeeded, and failed values.
| Event | Application action | Create another generation? |
|---|---|---|
| POST accepted with task ID | Save the task ID in a durable job row. | No |
| Status is processing | Schedule the next status read. | No |
| Status is succeeded | Validate result fields and begin the durable asset handoff. | No |
| Status is failed | Save the provider error and show a deliberate retry choice. | Not automatically |
| Status GET times out or returns a transient 5xx | Retry the same GET under a bounded policy. | No |
| Status GET returns 429 | Keep the same task ID and reschedule after the required wait. | No |
| Status GET returns 401, 403, or an unexplained 404 | Stop the worker and fix credentials, access, path, or stored ID. | No |
The invariant is stronger than a retry rule: one accepted application request maps to one provider task until a person or explicit business policy chooses otherwise.
Budget polling before you add workers
The current Sonilo machine-readable API reference lists standard-tier defaults of 60 requests per minute and five concurrent generations, while noting that account limits can differ. The OpenAPI description recommends a two-to-five-second polling interval with a bounded timeout.
Use this calculation before load testing:
Poll requests per minute = active jobs × 60 ÷ interval in seconds
| Active jobs | Poll interval | Nominal poll requests per minute | Result against a 60 RPM default |
|---|---|---|---|
| 1 | 2 seconds | 30 | Leaves 30 RPM before other calls. |
| 3 | 3 seconds | 60 | Uses the full default budget. |
| 5 | 5 seconds | 60 | Uses the full default budget at five active jobs. |
| 5 | 3 seconds | 100 | Exceeds the default. |
| 5 | 2 seconds | 150 | Exceeds the default by 2.5×. |
This is a planning model, not a service-level promise. It excludes submissions, account reads, retries, and every other request. Add scheduling jitter, reserve headroom, and read the live account limits rather than treating 60 and five as universal constants.
For a small integration, a five-second interval is a reasonable starting point when all five default generation slots are busy. For a larger one, schedule polls centrally so multiple web processes do not each poll the same task.
Store enough state to recover after a restart
An in-memory loop works in a demo. A production worker needs a durable job row so a process restart does not lose the only path to the result.
Store at least:
- your immutable request ID
- the provider task ID
- endpoint and API version
- current provider status
- first-submitted, last-polled, and next-poll timestamps
- total status reads and consecutive read errors
- a wall-clock deadline
- terminal error code and message
- output validation state
- durable asset ID after the file is copied
Use an atomic lease or compare-and-set update so two workers cannot own the same due job. The worker should select rows whose `next_poll_at` has passed, claim one briefly, read status once, write the new state, and release the lease.
Retry status reads without creating a retry storm
A 429 Too Many Requests response is a scheduling signal, not a failed generation. The Sonilo reference directs clients to honor Retry-After when present. RFC 9110 defines Retry-After as the server's requested delay before a follow-up request.
For network failures and transient server errors, use exponential backoff with jitter, then cap both the delay and the total retry window. The AWS Well-Architected Framework recommends backoff, jitter, and a maximum retry value because unbounded synchronized retries can intensify an overloaded service.
Keep three clocks separate:
- Request timeout: how long one GET may wait for a response.
- Retry window: how long transient status-read errors may be retried.
- Job deadline: how long your product waits before moving the local job to manual reconciliation.
Crossing a local deadline does not prove the provider job failed. Preserve the task ID and mark the application job as needing reconciliation so a later read can recover the real outcome.
Validate success before showing a finished file
A successful status is necessary, but your product should also require the expected result field and a non-empty media response. Different async endpoints can return audio, video, an output URL, or a language-to-output map, so validate against the endpoint contract in the current OpenAPI spec.
After validation, copy the result into storage your application controls. The separate guide on keeping AI music files after temporary API URLs expire covers byte counts, hashes, readback, and durable URLs. Do not mix that asset state with the provider's generation state.
| Provider state | Local job state | Local asset state |
|---|---|---|
| processing | waiting | none |
| succeeded, result not yet checked | validating | none |
| succeeded, media copy in progress | complete | copying |
| succeeded, readback passed | complete | ready |
| failed | failed | none |
| local deadline reached | needs reconciliation | unknown |
This three-state view prevents a common product bug: telling the user a file is ready when the provider finished but the application has not preserved or verified it.
Test failure paths before launch
Run controlled tests for each branch your worker claims to handle:
- A normal processing-to-success transition.
- A provider-reported failure.
- A status GET that times out, then succeeds.
- A 429 with `Retry-After`.
- Invalid credentials and insufficient access.
- An unknown task ID.
- A worker restart while a task is processing.
- Two workers racing for the same job.
- A local deadline followed by later provider success.
- A success response whose media field is missing or empty.
The test passes only when each application request retains one provider task ID, no status-read failure creates new work, and every terminal state remains explainable after a restart.
When polling is not the right design
Use a streaming response when the endpoint supports it and your server can hold the connection reliably. The Sonilo video-to-music guide explains the broader request workflow, while the current video-to-music endpoint reference distinguishes streaming from async mode.
If another provider offers signed, replayable webhooks, event delivery may reduce idle reads. Still keep a reconciliation poll because webhooks can arrive late, arrive more than once, or be missed by your application. For human-supervised one-off jobs, a dashboard refresh may be enough. For large queues, use a scheduler or queue with delayed delivery instead of one timer per web process.
Ship the worker contract before the UI
Write down the task mapping, poll budget, terminal-state rules, retry window, job deadline, and media acceptance check before the first customer request. Then expose only states your backend can recover and explain.
If your product starts with an edited video, review the Sonilo video-to-music API reference. Choose async mode only after the polling worker, durable job row, and output handoff are ready together.


