ChangelogNew

One API for every AI answer engine

API
A request to POST /v1/async/task, the queued task it returns and the finished task delivered by webhook

Send a prompt to any supported AI engine with one request and receive the answer as structured JSON, by webhook or by polling.

What you get

  • One endpoint, POST /v1/async/task, for every engine. Pick the engine with taskType and the market with country.
  • The same envelope for every engine: the task’s id, status and timestamps around an engine-specific response.
  • Results delivered your way: a webhook to your server the moment a task finishes, or GET /v1/async/task/{id} whenever you are ready.
  • Safe retries with idempotencyKey: sending the same key again returns the task you already created.

How it works

AI engines take seconds to minutes to answer, so every request becomes a durable task. A task moves from QUEUED to PROCESSING and ends as COMPLETED with the answer or FAILED with the reason. A temporary engine error sends the task back to the queue for another attempt, so short hiccups are retried before they ever reach you.

Add webhook.url and we post the finished task to your server, with up to five attempts over about 30 minutes. Finished tasks also stay available for polling for 24 hours, so a webhook and a poll together cover every result.

Where it helps

  • Run brand visibility checks from your own backend on any schedule.
  • Send one prompt to several engines and compare the answers side by side.
  • Keep long jobs simple: submit, move on and let the webhook bring the result back.

Send a request

Send the request with your API key in the Authorization header.

curl -X POST https://api.querying.ai/v1/async/task \
  -H "Authorization: Bearer $QUERYING_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "taskType": "CHATGPT",
    "payload": {
      "prompt": "Best coffee shops near me for working on a laptop",
      "country": "US"
    },
    "webhook": { "url": "https://your-server.example.com/hooks/querying" },
    "idempotencyKey": "laptop-cafes-us-0001"
  }'

Request fields

taskTypestringRequired
The engine to run, such as CHATGPT or GOOGLE.
payloadobjectRequired
The engine input: prompt or query, plus country and the engine’s options.
webhook.urlstring
An HTTPS address on your server. The finished task is posted there the moment it completes.
idempotencyKeystring
Your own ID for the request. Each key creates one task per account, so a retried send stays a single task.

Response

The call answers at once with the task in QUEUED. credits stays at 0 until the task finishes, and idempotencyKey comes back as you sent it.

JSON
{
  "success": true,
  "task": {
    "id": "…",
    "taskType": "CHATGPT",
    "status": "QUEUED",
    "priority": 1,
    "createdAt": "…",
    "idempotencyKey": "laptop-cafes-us-0001"
  },
  "credits": { "creditsToCharge": 0, "creditsCharged": 0 }
}

Receive the result

Read the task with GET /v1/async/task/{id} every few seconds until status is COMPLETED or FAILED. Results stay available for 24 hours after a task finishes.

curl https://api.querying.ai/v1/async/task/$TASK_ID \
  -H "Authorization: Bearer $QUERYING_API_KEY"

A finished task carries the charged credits and the engine’s response. This one is real, trimmed for length.

JSON
{
  "success": true,
  "task": {
    "id": "e5aba119-a26e-4f82-a965-35c9234a428b",
    "taskType": "CHATGPT",
    "status": "COMPLETED",
    "createdAt": "2026-10-02T15:41:03.858Z"
  },
  "credits": { "creditsToCharge": 2, "creditsCharged": 2 },
  "response": {
    "text": "Here are several **nearby cafés that look particularly suitable for laptop work**, based on current local listings and work-friendly details:\n…",
    "sources": [
      {
        "position": 1,
        "url": "https://awifiplace.com/cities/new-york",
        "label": "Best Coffee Shops to Work in New York (2026) | 103 Cafes with WiFi"
      }
    ],
    "searchQueries": [
      "best coffee shops for laptop work near me wifi outlets"
    ]
  }
}
task.statusstring
QUEUED, PROCESSING, then COMPLETED with the answer or FAILED with error.
credits.creditsChargedinteger
Credits charged for the finished task. A failed task charges 0.

Webhooks

With webhook.url, the finished task arrives at your server as a POST with the same task, credits and response that polling returns. A failed task arrives the same way, with response.error and 0 credits charged. Three headers sign every delivery:

querying-webhook-idstring
The task ID. A delivery arrives at least once, so use it to skip a repeat.
querying-webhook-timestampstring
Unix seconds when the delivery was signed. Accept deliveries signed within the last 5 minutes.
querying-webhook-signaturestring
v1, and a base64 HMAC-SHA256 of {id}.{timestamp}.{body}, keyed with the SHA-256 digest of your API key.

Check the signature on the raw body, before parsing it, and accept the delivery when it matches:

import base64
import hashlib
import hmac
import os
import time


def verify_webhook(headers, body: bytes) -> bool:
    """body is the raw request body, before json.loads."""
    task_id = headers["querying-webhook-id"]
    timestamp = headers["querying-webhook-timestamp"]
    key = hashlib.sha256(os.environ["QUERYING_API_KEY"].encode()).digest()
    signed = f"{task_id}.{timestamp}.".encode() + body
    digest = hmac.new(key, signed, hashlib.sha256).digest()
    expected = "v1," + base64.b64encode(digest).decode()
    given = headers.get("querying-webhook-signature", "")
    fresh = abs(time.time() - int(timestamp)) <= 300
    return fresh and hmac.compare_digest(expected, given)

Answer with a 2xx status within 30 seconds. Otherwise the delivery comes again after 2, 4, 8 and 16 minutes, five attempts in all, each with the same querying-webhook-id.

Errors

A refused request answers at once with success set to false and an error holding code, message and, for field errors, details. It stays out of the queue and costs 0 credits.

VALIDATION_ERRORHTTP 400
A field is missing or out of range. details names the field.
MISSING_API_KEYHTTP 401
The Authorization header is missing, or its key is unknown, expired or revoked.
INSUFFICIENT_CREDITSHTTP 402
The balance is below the price of the task. Add credits or upgrade the plan, then send again.
KEY_SCOPE_DENIEDHTTP 403
The engine is outside the key’s Allowed engines. Use another key, or add the engine in Manage key.
RESOURCE_ALREADY_EXISTSHTTP 409
A task with this idempotencyKey already exists on your account, so the send stays one task. In a batch, the item counts as a success.
PAYLOAD_TOO_LARGEHTTP 413
The body is over 1 MiB, or over 8 MiB for a batch.
REGION_UNSUPPORTEDHTTP 422
country is outside the engine’s list of countries. Sending again gives the same answer, so details.retryable is false.
PLAN_CONCURRENCY_LIMITHTTP 429
Your queued and running tasks reached the plan’s limit: 2 on the free plan, 10 on Basic, 25 on Pro, 50 on Scale and 100 on Max. Send again after Retry-After seconds.
KEY_DAILY_CREDIT_LIMITHTTP 429
The key used up its Daily credit limit. Retry-After runs until the next midnight UTC.
QUEUE_BACKPRESSUREHTTP 429
The engine’s queue is full for the moment. Send again after Retry-After seconds.

Pricing

Each engine has its own credit price, and a task is charged when it completes. A failed task releases the credits it reserved.

More updates

Stay in the loop

Get new product updates in your inbox.

RSS feed