Skip to content

Webhook Mode

Webhook mode returns a task ID immediately and sends the result to your callback URL when the task completes. No polling needed.

Prerequisites

Make sure you have an API key. See Get Started with API for setup instructions.

Step 1: Submit a Task with Webhook

Add ?webhook=YOUR_CALLBACK_URL to the submit endpoint:

bash
curl -X POST "https://api.get3w.com/v1/google/nano-banana-pro/text-to-image?webhook=https://your-server.com/callback" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "A sunset over mountains",
    "aspect_ratio": "16:9",
    "resolution": "1k",
    "output_format": "png",
    "channel": "stable"
  }'
python
import requests

response = requests.post(
    "https://api.get3w.com/v1/google/nano-banana-pro/text-to-image",
    params={"webhook": "https://your-server.com/callback"},
    headers={
        "Authorization": "Bearer YOUR_API_KEY",
        "Content-Type": "application/json"
    },
    json={
        "prompt": "A sunset over mountains",
        "aspect_ratio": "16:9",
        "resolution": "1k",
        "output_format": "png",
        "channel": "stable"
    }
)

task = response.json()
print(task["id"])  # Task is running; result will be sent to your webhook
javascript
const apiKey = "YOUR_API_KEY";
const webhookUrl = encodeURIComponent("https://your-server.com/callback");

const response = await fetch(
    `https://api.get3w.com/v1/google/nano-banana-pro/text-to-image?webhook=${webhookUrl}`,
    {
        method: "POST",
        headers: {
            "Authorization": `Bearer ${apiKey}`,
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            prompt: "A sunset over mountains",
            aspect_ratio: "16:9",
            resolution: "1k",
            output_format: "png",
            channel: "stable"
        })
    }
);

const task = await response.json();
console.log(task.id);  // Result will be sent to your webhook

Response (immediate):

json
{
  "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "created",
  "estimated_duration": 52
}

Step 2: Receive the Webhook

When the task completes (or fails), Get3W sends a POST request to your callback URL with the result:

json
{
  "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "model": "google/nano-banana-pro/text-to-image",
  "status": "completed",
  "code": 0,
  "created_at": "2026-03-28T07:50:42",
  "input": {
    "prompt": "A sunset over mountains",
    "aspect_ratio": "16:9",
    "resolution": "1k",
    "output_format": "png",
    "channel": "stable"
  },
  "outputs": [
    "https://storage.example.com/output.png"
  ],
  "error": null
}

A failure carries the same keys — outputs is an empty array and error is populated:

json
{
  "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "model": "google/nano-banana-pro/text-to-image",
  "status": "failed",
  "code": 1400,
  "created_at": "2026-03-28T07:50:42",
  "input": { "prompt": "A sunset over mountains" },
  "outputs": [],
  "error": "Invalid input: prompt is required"
}

Every delivery carries all eight keys, so you can branch on status without checking for missing fields.

Step 3: Handle the Webhook

Here's a minimal server that receives webhook callbacks:

python
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/callback", methods=["POST"])
def webhook_handler():
    data = request.json

    if data["status"] == "completed":
        print(f"Task {data['id']} completed!")
        print(f"Outputs: {data['outputs']}")
    elif data["status"] == "failed":
        print(f"Task {data['id']} failed: {data['error']}")

    return jsonify({"received": True}), 200

if __name__ == "__main__":
    app.run(port=3000)
javascript
const express = require("express");
const app = express();

app.use(express.json());

app.post("/callback", (req, res) => {
    const data = req.body;

    if (data.status === "completed") {
        console.log(`Task ${data.id} completed!`);
        console.log(`Outputs: ${data.outputs}`);
    } else if (data.status === "failed") {
        console.log(`Task ${data.id} failed: ${data.error}`);
    }

    res.json({ received: true });
});

app.listen(3000, () => console.log("Webhook server running on port 3000"));

Webhook Payload Fields

FieldTypeDescription
idstringTask ID
modelstringModel slug (e.g. google/nano-banana-pro/text-to-image)
statusstringcompleted or failed
codeint0 on success; see Error Codes on failure
created_atstringISO 8601 timestamp
inputobjectThe original request parameters
outputsstring[]Output URLs; empty array when the task failed
errorstringError message; null when the task succeeded

Retry Policy

Get3W makes up to 3 delivery attempts if your server returns a non-2xx status or is unreachable: the first immediately, then after 2 seconds, then after 4 seconds. Each attempt times out after 30 seconds.

After the third attempt the delivery is abandoned. The result is not lost — fetch it with GET /v1/requests/{request_id} while the task record is retained. For that reason, treat webhooks as a fast path and keep a reconciliation poll for tasks you never got a callback for.

When to Use Webhook Mode

  • Server-to-server integrations — No need to keep a connection open or run a polling loop
  • Event-driven architectures — Trigger downstream workflows when a task completes
  • High-throughput pipelines — Submit hundreds of tasks and process results as they arrive

TIP

Your webhook URL must be publicly accessible. For local development, use a tunneling tool like ngrok to expose your local server.

Next Steps

  • Sync Mode — Simplest approach for quick tasks
  • Async Mode — Poll for results instead of using webhooks

Released under the MIT License.