Skip to content

New Official SDKs for TypeScript, Python and Go

SerpKite
Get API key
Docs menu / Batch requests

Batch requests

Queue up to 100 requests per call with POST /v1/batches at half the credits. Poll GET /v1/batches/{id}, wait with the SDKs, or receive a signed webhook.

View as Markdown

When you don’t need the answer while you wait (rank tracking, nightly enrichment, dataset building), send the work to the batch lane. POST /v1/batches queues up to 100 requests for one endpoint and answers right away. Each request becomes its own job, costs half the realtime price, and is delivered by polling or by webhook.

Realtime (POST /v1/search, …) Batch (POST /v1/batches)
Requests per call 1 1 to 100, all for one endpoint
Response 200 with the result 202 with one job per request
Latency Seconds Target: done within 15 minutes
Price Normal price 0.5× the realtime price
Delivery In the response Poll GET /v1/batches/{id} or webhook

Realtime endpoints take one JSON object per call. A JSON array body is rejected with 400 invalid_request; to run many queries, either send them in parallel within your rate limit or queue them here.

Create a batch

Send the endpoint name, the request bodies, and optionally a webhook_url:

curl https://api.serpkite.com/v1/batches \
  -H "Authorization: Bearer $SERPKITE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"endpoint":"search","requests":[{"q":"best espresso machine","country":"us"},{"q":"best espresso machine","country":"de","language":"de"}],"webhook_url":"https://example.com/hooks/serpkite"}'

# then poll a job (free)
curl https://api.serpkite.com/v1/batches/0192f7a4-6c1e-7b3a-9d2f-5e8a1c4b7d90 \
  -H "Authorization: Bearer $SERPKITE_API_KEY"
Field Type Description
endpoint required string One of search, images, videos, news, maps, places, reviews, shopping, scholar, patents, autocomplete, lens, webpage.
requests required array 1 to 100 request objects. Each takes the same fields as the realtime endpoint, including num: 100, format, fields, include_content and max_age.
webhook_url string HTTPS URL that receives each finished job. Defaults to the account webhook set in the dashboard. See Webhooks.

The response is 202 Accepted with a batches array: one entry per request, in the same order. An entry is either a job or, when that request was invalid or could not be queued, an error object. One invalid request doesn’t reject the rest.

{
  "batches": [
    {
      "id": "0192f7a4-6c1e-7b3a-9d2f-5e8a1c4b7d90",
      "status": "queued",
      "endpoint": "/v1/search",
      "created_at": "2026-09-29T08:00:00Z",
      "completed_at": null,
      "credits_used": 0,
      "poll_url": "https://api.serpkite.com/v1/batches/0192f7a4-6c1e-7b3a-9d2f-5e8a1c4b7d90",
      "webhook_url": "https://example.com/hooks/serpkite",
      "webhook_status": "pending"
    },
    {
      "error": {
        "code": "invalid_request",
        "message": "unknown parameter \"type\": use the endpoint path",
        "request_id": "req_01J8ZK4M6Q2V7:1"
      }
    }
  ]
}

Requests are validated with the same strict rules as realtime calls: an unknown or renamed parameter fails that entry with a message naming the replacement.

Safe retries with Idempotency-Key

A timeout on POST /v1/batches doesn’t tell you whether the jobs were queued. Send an Idempotency-Key header (1 to 255 printable ASCII characters, for example a UUID you store with the batch) and retry with the same key and the same body: for 24 hours SerpKite returns the first response, with Idempotent-Replayed: true, instead of queueing and billing the jobs again.

curl https://api.serpkite.com/v1/batches \
  -H "Authorization: Bearer $SERPKITE_API_KEY" \
  -H "Idempotency-Key: 5f0c7d52-8a8e-4f7e-9d7b-1b6f0f3c2a91" \
  -H "Content-Type: application/json" \
  -d '{"endpoint":"search","requests":[{"q":"espresso grinder"}]}'

The same key with a different body gets 422 idempotency_key_reused; a retry that arrives while the first call is still running gets 409 (retry shortly). The SDKs take the key as an option (idempotencyKey, idempotency_key, serpkite.WithIdempotencyKey) and, when it is set, also retry server errors and dropped connections.

Pricing

Batch jobs cost half the realtime price, rounded up to the next 0.001 credit:

Request Realtime Batch
search (1 page) 1 0.5
search with num: 100 7 3.5
search with include_content: 3 4 2

The maximum cost of each job is reserved from your balance when it is queued, so a queued job can’t fail later for lack of credits. The jobs of one call are reserved together: if your balance, spend cap, key limit or free daily limit can’t cover all of them, none is queued and each valid entry carries the error (for example insufficient_credits). If a job fails, or costs less than reserved (for example fewer pages came back), the difference is refunded. Queuing is checked against your key limit and spend cap just like a realtime call. The POST /v1/batches call itself counts once against your per-second rate limit, whatever its size; the jobs are paced by the batch lane.

Get the results

Wait with an SDK

The SDKs poll for you: sk.batches.wait(id) in TypeScript, sk.batches.wait(id) in Python and c.Batches.Wait(ctx, id) in Go return the job once it is done or failed. See SDKs.

Poll

Poll GET /v1/batches/{id} (or the job’s poll_url) until status is done or failed. Polling is free.

status Meaning
queued Waiting for a worker.
running Being fetched.
done Finished; result holds the same body the realtime endpoint returns (request, results, meta, …).
failed Could not be completed; error has code and message, and the credits were refunded.
curl https://api.serpkite.com/v1/batches/0192f7a4-6c1e-7b3a-9d2f-5e8a1c4b7d90 \
  -H "Authorization: Bearer $SERPKITE_API_KEY"
{
  "id": "0192f7a4-6c1e-7b3a-9d2f-5e8a1c4b7d90",
  "status": "done",
  "endpoint": "/v1/search",
  "created_at": "2026-09-29T08:00:00Z",
  "completed_at": "2026-09-29T08:03:12Z",
  "credits_used": 0.5,
  "poll_url": "https://api.serpkite.com/v1/batches/0192f7a4-6c1e-7b3a-9d2f-5e8a1c4b7d90",
  "webhook_url": null,
  "webhook_status": null,
  "error": null,
  "result": {
    "request": { "endpoint": "search", "engine": "google", "q": "best espresso machine", "country": "us" },
    "results": [{ "position": 1, "title": "…", "link": "https://www.example.com/…", "domain": "example.com" }],
    "related_searches": [],
    "meta": { "request_id": "req_01J8ZK4M6Q2V7", "credits_used": 0.5, "cached": false }
  }
}

Poll every few seconds at most; jobs don’t finish faster if you poll harder. For large volumes, prefer webhooks.

Webhooks

Set webhook_url on the batch (or a default webhook URL in the dashboard under Settings) and SerpKite POSTs each finished job to it, always signed with your account’s webhook secret. Then you don’t need to poll at all. Delivery format, signature verification and retries are covered in Webhooks.

Limits and retention

  • 1 to 100 requests per POST /v1/batches call, all for the same endpoint.
  • Up to 10,000 queued jobs per account. Beyond that, new batches get 429 rate_limited until some finish.
  • Jobs target completion within 15 minutes. That is a target for the lane, not a per-job guarantee. Workers take jobs round-robin across accounts, so a large backlog from one account doesn’t hold up another’s jobs.
  • A job’s result is kept for 24 hours after it finishes, then deleted. The stored request (which contains your query) is deleted as soon as the job completes.
  • You can list recent jobs and their status in the dashboard.

Related: Batches endpoint, Webhooks, Rank tracking, Credits and billing.

Last updated: 2026-09-29