> ## Documentation Index
> Fetch the complete documentation index at: https://docs.querying.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 태스크 대량 제출: 배치와 위치 그리드

> 500개짜리 배치, 10,000개짜리 배치 객체, 위치 그리드 중에서 고르고, 제출하고, 결과를 읽습니다.

세 가지 엔드포인트가 한 번의 요청으로 여러 태스크를 만듭니다. 모두 일반 태스크를 만들므로 폴링, webhook, 크레딧, 요금제 한도는 단건 태스크와 같습니다.

## 세 엔드포인트 비교

| | `POST /v1/async/task/batch` | `POST /v1/batches` | `POST /v1/async/task/grid` |
| - | - | - | - |
| 보내는 내용 | 태스크 요청의 JSON 배열 | `{ items, webhook?, metadata?, idempotencyKey? }` | 태스크 요청 하나와 `grid` |
| 최대 태스크 수 | 500 | 10,000 | 81(9 × 9 그리드) |
| 최대 본문 | 8 MiB | 32 MiB | 1 MiB |
| 성공 상태 | `200` | 생성 시 `202`, 반복된 `idempotencyKey`면 `200` | `200` |
| 검증 | 항목별로 따로. 잘못된 항목은 그 자리에서 실패 | 모든 항목을 먼저 검증. 하나라도 잘못되면 배치 전체를 `400`으로 거부하고 아무것도 저장하지 않음 | 중심 지점을 먼저. 중심이 거부되면 그리드 전체가 거부됨 |
| 요금제 한도 초과 시 | 초과 항목은 `PLAN_CONCURRENCY_LIMIT`로 실패하며 직접 다시 제출 | 항목이 서버에서 기다렸다가 여유가 생기면 내보내짐 | 초과 지점은 그 자리에서 실패 |
| 진행 상황 | 태스크마다 폴링 | `GET /v1/batches/{id}`의 개수 | `GET /v1/async/task/grid/{id}` |
| 완료 신호 | 태스크마다 webhook 하나 | 태스크마다 webhook 하나, 이어서 `batch.completed` | 태스크마다 webhook 하나 |

## 어떤 것을 쓸까

* **태스크 500개 이하, 재시도는 직접 처리:** `POST /v1/async/task/batch`. 응답에서 모든 태스크 id를 받습니다.
* **요금제 동시 실행 한도보다 많은 태스크, 또는 매일 수천 개를 돌리는 작업:** `POST /v1/batches`. 서버가 항목을 보관했다가 [요금제 한도](/ko/concepts/credits-and-limits.md#요금제-동시-실행-한도)와 엔진별 대기열이 허용하는 만큼 넣어 주므로 `429`나 `503`을 직접 재시도할 필요가 없습니다.
* **한 장소 주변 여러 지점에서 같은 프롬프트를 묻기:** `POST /v1/async/task/grid`.

## `POST /v1/async/task/batch`

태스크 요청을 최대 500개까지 배열로 보냅니다([레퍼런스](/ko/api-reference/create-task-batch.md)). 엔진을 섞을 수 있습니다.

```bash theme={"theme":"css-variables"}
curl -X POST https://api.querying.ai/v1/async/task/batch \
  -H "Authorization: Bearer $QUERYING_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[
    { "taskType": "CHATGPT", "idempotencyKey": "job-42:p1:chatgpt", "payload": { "prompt": "best CRM for a small team", "country": "US" } },
    { "taskType": "GEMINI", "idempotencyKey": "job-42:p1:gemini", "payload": { "prompt": "best CRM for a small team", "country": "US" } }
  ]'
```

형식이 올바른 요청은 `200`을 반환합니다. `results[i].index`는 배열에서의 위치와 같고, 각 결과에는 `task`나 `error` 중 하나가 있습니다.

```json theme={"theme":"css-variables"}
{
  "success": true,
  "summary": { "total": 2, "succeeded": 1, "failed": 1 },
  "results": [
    { "success": true, "index": 0, "task": { "id": "8f2c1e40-...", "status": "QUEUED", "...": "..." }, "credits": { "creditsToCharge": 0, "creditsCharged": 0 } },
    {
      "success": false,
      "index": 1,
      "error": {
        "code": "PLAN_CONCURRENCY_LIMIT",
        "message": "free plan allows 2 concurrent tasks; 2 already active",
        "details": { "plan": "free", "limit": 2, "source": "plan", "active": 2, "retryAfterSec": 5 },
        "timestamp": "2026-10-19T04:12:00.000Z"
      }
    }
  ]
}
```

다음 경우에는 요청 전체가 거부되고 태스크가 하나도 만들어지지 않습니다.

| 상태 | 코드 | 경우 |
| - | - | - |
| `400` | `VALIDATION_ERROR` | 올바른 JSON이 아니거나, 배열이 아니거나, 비어 있거나, 항목이 500개를 넘음 |
| `413` | `PAYLOAD_TOO_LARGE` | 본문이 8 MiB를 넘음 |
| `503` | `SERVER_OVERLOADED` | 접수가 포화 상태입니다. `Retry-After` 뒤에 재시도하세요. |
| `503` | `SERVER_OVERLOADED` | 전체 대기열이 가득 찼습니다. `Retry-After` 뒤에 재시도하세요. |

반복된 `idempotencyKey`는 기존 태스크를 성공으로 반환합니다. [멱등성](/ko/concepts/idempotency.md)을 참고하세요.

## `POST /v1/batches`

항목을 최대 10,000개까지 보냅니다([레퍼런스](/ko/api-reference/create-batch.md)). 각 항목은 태스크 필드 `taskType`, `payload`, `priority`, `idempotencyKey`와, 결과에 그대로 돌아오는 선택 필드 `customId`(최대 128자)를 받습니다. `webhook.url`은 항목이 아니라 배치에 지정하세요.

```bash theme={"theme":"css-variables"}
curl -X POST https://api.querying.ai/v1/batches \
  -H "Authorization: Bearer $QUERYING_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "idempotencyKey": "daily-2026-10-19",
    "webhook": { "url": "https://hooks.example.com/querying" },
    "metadata": { "run": "daily" },
    "items": [
      { "customId": "crm-chatgpt-us", "taskType": "CHATGPT", "payload": { "prompt": "best CRM for a small team", "country": "US" } },
      { "customId": "crm-gemini-us", "taskType": "GEMINI", "payload": { "prompt": "best CRM for a small team", "country": "US" } }
    ]
  }'
```

응답은 배치를 담은 `202`입니다. 첫 항목들은 이미 제출되어 있습니다.

```json theme={"theme":"css-variables"}
{
  "success": true,
  "batch": {
    "id": "3f6b2a10-...",
    "status": "OPEN",
    "total": 2,
    "counts": { "pending": 0, "queued": 2, "processing": 0, "completed": 0, "failed": 0, "rejected": 0 },
    "createdAt": "2026-10-19T09:00:00.000Z",
    "completedAt": null,
    "idempotencyKey": "daily-2026-10-19",
    "webhook": { "url": "https://hooks.example.com/querying" },
    "metadata": { "run": "daily" }
  }
}
```

### 봉투 규칙

| 필드 | 규칙 |
| - | - |
| `items` | 객체 1\~10,000개 |
| `webhook.url` | 태스크와 같은 [URL 규칙](/ko/concepts/webhooks.md#url-규칙)을 따릅니다. 모든 항목에 적용됩니다. |
| `metadata` | JSON으로 최대 8 KB인 객체이며 그대로 반환됩니다 |
| `idempotencyKey` | 1\~256자. 반복하면 첫 배치를 `200`으로 반환합니다. |
| 항목 `idempotencyKey` | 배치 안에서 고유해야 합니다 |
| 항목 `webhook` | 거부됩니다. 배치의 `webhook.url`을 쓰세요. |

### 전부 아니면 전무 검증

아무것도 저장하기 전에 모든 항목에 단건 태스크와 같은 검사를 합니다. 필드, 키 범위, 비공개 엔진, 전용 엔드포인트, 지역, `payload.location`이 대상입니다. 하나라도 실패하면 응답은 `400 VALIDATION_ERROR`이고, 아무것도 저장되지 않으며, `details`에 실패한 항목이 최대 50개까지 나열됩니다.

```json theme={"theme":"css-variables"}
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "1 of 2 items are invalid; the batch was not stored",
    "details": {
      "invalidItems": 1,
      "items": [
        { "index": 1, "code": "VALIDATION_ERROR", "message": "Request validation failed", "details": [{ "field": "payload.prompt", "message": "payload.prompt or payload.query is required" }] }
      ]
    },
    "timestamp": "2026-10-19T09:00:00.000Z"
  }
}
```

### 항목 실행 방식

배치는 요금제 한도와 엔진 대기열에 여유가 있을 때 항목을 태스크로 내보냅니다. 계정끼리 차례를 나누므로 큰 배치가 다른 계정의 작은 배치를 막지 않습니다. 내보낸 항목은 단건 태스크와 같은 접수 과정을 거칩니다. 크레딧 보류를 걸고, 키의 일일 크레딧 한도에 포함됩니다.

항목이 거부되면 코드에 따라 처리가 정해집니다.

| 코드 | 항목 |
| - | - |
| `PLAN_CONCURRENCY_LIMIT`, `KEY_DAILY_CREDIT_LIMIT`, `SERVER_OVERLOADED`, `DB_UNAVAILABLE`, `ENQUEUE_ERROR` | `PENDING`으로 남아 나중에 다시 시도됩니다 |
| `QUEUE_BACKPRESSURE`, `ENGINE_UNAVAILABLE` | `PENDING`으로 남아 나중에 다시 시도됩니다 |
| `API_KEY_INACTIVE` | `REJECTED`: 배치의 키가 폐기 또는 만료되었거나 하위 계정이 정지되었습니다 |
| 그 밖의 코드(`INSUFFICIENT_CREDITS` 포함) | `REJECTED` |

대기 중인 항목이 없고 모든 태스크가 끝나면 배치는 `COMPLETED`가 됩니다. `webhook.url`이 있으면 서버가 이어서 [`batch.completed` 이벤트](/ko/concepts/webhooks#배치-완료-이벤트)를 보냅니다.

### 진행 상황과 결과 읽기

`GET /v1/batches/{id}`는 항목 상태별 `counts`(`pending`, `queued`, `processing`, `completed`, `failed`, `rejected`)와 함께 배치를 반환합니다. 이 엔드포인트를 폴링하면 대기 중인 항목도 진행됩니다.

```bash theme={"theme":"css-variables"}
curl https://api.querying.ai/v1/batches/3f6b2a10-... \
  -H "Authorization: Bearer $QUERYING_API_KEY"
```

`GET /v1/batches/{id}/items`는 항목을 제출 순서대로 나열합니다.

```bash theme={"theme":"css-variables"}
curl "https://api.querying.ai/v1/batches/3f6b2a10-.../items?status=completed&include=response&limit=20" \
  -H "Authorization: Bearer $QUERYING_API_KEY"
```

| 쿼리 | 설명 |
| - | - |
| `status` | `pending`, `queued`, `processing`, `completed`, `failed`, `rejected` 중 하나 |
| `include=response` | 끝난 태스크마다 `response` 또는 `error`를 추가합니다 |
| `limit` | 기본 100, 최대 1,000. `include=response`일 때는 기본 20, 최대 100. |
| `cursor` | 이전 페이지의 `nextCursor` |

```json theme={"theme":"css-variables"}
{
  "success": true,
  "items": [
    {
      "index": 0,
      "customId": "crm-chatgpt-us",
      "taskType": "CHATGPT",
      "status": "COMPLETED",
      "taskId": "8f2c1e40-...",
      "completedAt": "2026-10-19T09:01:40.000Z",
      "response": { "text": "...", "sources": [] }
    }
  ],
  "nextCursor": null
}
```

`REJECTED` 항목에는 `error: { code, message }`가 담깁니다. 결과 본문은 [비동기 태스크](/ko/concepts/async-tasks.md#보존-기간)의 24시간 폴링 기간 안에서만 포함됩니다. [배치 조회](/ko/api-reference/get-batch.md)와 [배치 항목 목록](/ko/api-reference/list-batch-items.md)도 참고하세요.

## `POST /v1/async/task/grid`

중심 주변 정사각형 그리드의 모든 지점에서 같은 프롬프트를 묻습니다([레퍼런스](/ko/api-reference/create-task-grid.md)). 각 지점은 그 지점을 `payload.location`으로 하는 태스크가 되므로, 엔진은 위치를 받는 `CHATGPT`, `GEMINI`, `GOOGLE`, `AIMODE`, `GOOGLE_SERP` 중 하나여야 합니다. `payload.location`을 직접 보내지 마세요.

```bash theme={"theme":"css-variables"}
curl -X POST https://api.querying.ai/v1/async/task/grid \
  -H "Authorization: Bearer $QUERYING_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "taskType": "CHATGPT",
    "idempotencyKey": "soho-coffee-2026-10-19",
    "payload": { "prompt": "best coffee shops near me for working on a laptop", "country": "US" },
    "grid": { "center": { "latitude": 40.72288, "longitude": -73.99875 }, "size": 3, "radiusMeters": 1500 }
  }'
```

| 필드 | 규칙 |
| - | - |
| `grid.size` | 한 변의 지점 수 `3`, `5`, `7`, `9`(태스크 9\~81개) |
| `grid.radiusMeters` | 100\~50,000. 중심에서 각 변의 가운데까지의 거리입니다. 지점 간격은 `2 × radius ÷ (size − 1)`입니다. |
| `grid.center` | 십진 도 단위의 위도와 경도. 그리드는 위도 89도 미만에 있어야 합니다. |
| `idempotencyKey` | 선택, 1\~200자. 그리드 id가 됩니다. |
| `priority`, `webhook` | 모든 지점에 적용됩니다 |

0행은 북쪽 끝, 0열은 서쪽 끝입니다. 응답은 `grid`(`id`, `spacingMeters` 포함), `summary`, 그리고 행 순서의 `points`를 반환하며, 각 지점에는 `row`, `col`, `location`과 `task` 또는 `error`가 있습니다.

중심 지점이 먼저 단독으로 접수됩니다. 중심이 거부되면(예: `422 LOCATION_UNSUPPORTED`, `403 KEY_SCOPE_DENIED`, `402 INSUFFICIENT_CREDITS`, 요금제 한도 초과) 그 오류가 그리드 전체의 응답이 되고 태스크는 하나도 만들어지지 않습니다. 중심 이후 지점이 용량 때문에 거부되면 `success: false`로 그 자리에서 실패합니다.

그리드는 [`GET /v1/async/task/grid/{id}`](/ko/api-reference/get-task-grid)로 다시 읽습니다. 끝난 태스크의 결과가 필요하면 `?include=response`를 붙이세요. 접수되지 않은 지점은 없고, 다른 계정의 그리드 id는 `404`를 반환합니다. 같은 id로 같은 그리드를 다시 보내면 없는 지점만 접수됩니다. [멱등성](/ko/concepts/idempotency.md#위치-그리드)을 참고하세요.

## 다음 단계

> **[멱등성](/ko/concepts/idempotency.md)**
>
> 엔드포인트별 반복 키 처리.

> **[크레딧과 한도](/ko/concepts/credits-and-limits.md)**
>
> 요금제 동시 실행 한도와 속도 조절.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.