AnswerLine Sign in Start free

Engineering · Fundamentals

Sync calls, async tasks or webhooks: how to call an answer API

An AI assistant’s answer takes far longer than a typical API call: the engine runs the prompt, searches and writes. The API offers three ways to deal with that. All return the same result JSON, so the choice is about cost, failure modes and plumbing.

The examples read two environment variables: API_URL, your API base URL, and API_KEY.

Synchronous: one call, one answer

curl -X POST "$API_URL/v1/monitor/chatgpt" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Best CRM for small agencies", "country": "US"}'

The connection stays open until the answer is ready, and the body is {"success": true, "result": {...}}. The simplicity has costs:

Use it for interactive tools, one-off checks and agents calling through the MCP server.

Async with polling

curl -X POST "$API_URL/v1/async/task" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"taskType": "CHATGPT", "payload": {"prompt": "Best CRM for small agencies", "country": "US"}, "idempotencyKey": "crm-agencies-us-2026-09-15"}'

This answers at once with task.id and status QUEUED. The task waits for a slot instead of failing and costs the base price. Read it with GET /v1/async/task/{id} until the status is COMPLETED or FAILED:

import os, random, time
import httpx

api = httpx.Client(base_url=os.environ["API_URL"], headers={"Authorization": f"Bearer {os.environ['API_KEY']}"}, timeout=30)

def wait(task_id: str, limit_s: float = 600) -> dict:
    deadline, cap = time.monotonic() + limit_s, 0.5
    while time.monotonic() < deadline:
        res = api.get(f"/v1/async/task/{task_id}")
        if res.status_code == 200 and res.json()["task"]["status"] in ("COMPLETED", "FAILED"):
            return res.json()
        if 400 <= res.status_code < 500 and res.status_code != 429:
            res.raise_for_status()
        time.sleep(random.uniform(0, cap))
        cap = min(cap * 2, 10)
    raise TimeoutError(task_id)

The SDKs ship this loop as waitForTask and wait_for_task. Polling suits a script watching a handful of tasks; with thousands, polls eat into your rate limit and add delay between a task finishing and you reading it.

Async with webhooks

Add a webhook URL to the task and the finished task is POSTed to you:

{
  "taskType": "CHATGPT",
  "payload": { "prompt": "Best CRM for small agencies", "country": "US" },
  "idempotencyKey": "crm-agencies-us-2026-09-15",
  "webhook": { "url": "https://your-app.com/hooks/answers" }
}

The body is what GET /v1/async/task/{id} returns. Three rules keep a receiver correct:

  1. Verify the signature on the raw body before parsing; the webhooks guide shows how.
  2. Answer 2xx quickly, then work. A delivery without a 2xx within 15 seconds is retried with backoff.
  3. Deduplicate by task.id. Deliveries are concurrent and retried independently, so they can repeat and arrive out of order.
import json
from flask import Flask, abort, request

app = Flask(__name__)

@app.post("/hooks/answers")
def answers():
    raw = request.get_data()
    if not signature_valid(raw, request.headers.get("Webhook-Signature")):  # your check, from the guide
        abort(400)
    delivery = json.loads(raw)
    if not delivery.get("test"):
        jobs.put(delivery)  # your queue; store by delivery["task"]["id"], skipping ids already stored
    return "", 204

Choosing

SynchronousAsync + pollingAsync + webhook
PriceBase + surchargeBaseBase
Slots all busy429, retry laterWaits in queueWaits in queue
Tasks per request1Up to 500 in a batchUp to 500 in a batch
You runA long HTTP callA polling loopA public HTTPS endpoint
SuitsInteractive use, agentsScripts, small jobsPipelines, schedules

Whichever you pick, give each task an idempotencyKey derived from what it means (prompt, market, day). Retrying creation after a timeout is then safe: a duplicate is refused with 409 RESOURCE_CONFLICT instead of running twice.

Try it on your own prompts

500 free credits a month, no card. One POST returns the answer, sources and citations as JSON.

Keep reading