Skip to main content
POST
/
bulk
Bulk API
curl --request POST \
  --url https://text.external-api.pangram.com/bulk \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '
{
  "items": [
    {}
  ],
  "text": [
    {}
  ]
}
'
{
  "bulk_id": "<string>",
  "status": "<string>",
  "total_items": 123,
  "accepted_items": [
    {}
  ],
  "failed_items": [
    {}
  ],
  "accepted": 123,
  "succeeded": 123,
  "failed": 123,
  "created_at": "<string>",
  "completed_at": "<string>",
  "offset": 123,
  "limit": 123,
  "items": [
    {}
  ]
}
Current
Bulk API
Current version
The Bulk API queues many AI detection inputs as one asynchronous job. Submit the job with POST /bulk, poll GET /bulk/{bulk_id}, then page through item metadata or results.
Completion time depends on the number and length of submitted items and current system load. Use GET /bulk/{bulk_id} to monitor progress. Bulk jobs use the same base URL and API key authentication as the AI detection task API:
https://text.external-api.pangram.com
Terminal bulk statuses are succeeded, failed, and partial. Bulk metadata and results are retained for 48 hours after the job reaches a terminal status. Timestamps are returned as Unix epoch seconds encoded as strings, such as "1760000000.0". Treat them as UTC instants when converting to a date-time value. The launch bulk limit is 1,000 billable units per request. A billable unit is one started 1,000-word block per valid item, with a minimum of one unit per item. There is no separate item-count limit, but normal request-body limits still apply. Requests over the current bulk limit return 413 Payload Too Large.

POST /bulk

Create a bulk AI detection job.
POST https://text.external-api.pangram.com/bulk

Request

Provide exactly one of items or text. Valid request bodies use either a plain text list:
{"text": ["First text", "Second text"]}
Or an items list when you want customer IDs returned with status and results:
{"items": [{"id": "row-001", "text": "First text"}]}
Do not include both text and items in the same request. Item id values are optional, but must be unique when provided.
items
array
List of item objects. Each item must include text and may include a unique customer-defined id.
text
array
List of input text strings. Use this simpler shape when you do not need customer item IDs.

Response

Returns 202 Accepted.
bulk_id
string
The ID of the bulk job.
status
string
Initial status. Usually queued; returns failed if every item failed immediate validation.
total_items
integer
Total number of submitted items.
accepted_items
array
Items accepted for processing. Each item includes index, optional id, and task_id.
failed_items
array
Items that failed immediate validation. Each item includes index, optional id, task_id: null, stage, and error.

Example

curl -X POST https://text.external-api.pangram.com/bulk \
  -H "Content-Type: application/json" \
  -H "x-api-key: your_api_key_here" \
  -d '{
    "items": [
      {"id": "row-001", "text": "First text to analyze"},
      {"id": "row-002", "text": "Second text to analyze"}
    ]
  }'
Example Response
{
  "bulk_id": "blk_123",
  "status": "queued",
  "total_items": 2,
  "accepted_items": [
    {
      "index": 0,
      "id": "row-001",
      "task_id": "123e4567-e89b-12d3-a456-426614174000"
    },
    {
      "index": 1,
      "id": "row-002",
      "task_id": "223e4567-e89b-12d3-a456-426614174000"
    }
  ],
  "failed_items": []
}

GET /bulk/

Fetch the current status and counters for a bulk job.
GET https://text.external-api.pangram.com/bulk/{bulk_id}

Request

bulk_id
string
required
The bulk job ID returned by POST /bulk.

Response

bulk_id
string
The ID of the bulk job.
status
string
One of queued, running, succeeded, failed, or partial.
total_items
integer
Total number of submitted items.
accepted
integer
Number of items accepted for processing.
succeeded
integer
Number of items that completed successfully.
failed
integer
Number of items that failed.
created_at
string
Job creation timestamp as Unix epoch seconds encoded as a string.
completed_at
string
Job completion timestamp as Unix epoch seconds encoded as a string. null while the job is not terminal.
Example Response
{
  "bulk_id": "blk_123",
  "status": "partial",
  "total_items": 3,
  "accepted": 2,
  "succeeded": 2,
  "failed": 1,
  "created_at": "1760000000.0",
  "completed_at": "1760000030.0"
}

Status values

StatusDescription
queuedThe job was accepted, but no accepted item has started processing yet.
runningAt least one accepted item is in progress and the job is not terminal.
succeededEvery submitted item completed successfully.
failedEvery submitted item failed, including immediate validation failures.
partialEvery submitted item is terminal, with at least one success and at least one failure.

GET /bulk//items

Fetch paginated item metadata for a bulk job.
GET https://text.external-api.pangram.com/bulk/{bulk_id}/items?offset=0&limit=100

Request

bulk_id
string
required
The bulk job ID returned by POST /bulk.
offset
integer
default:"0"
Zero-based item offset.
limit
integer
default:"100"
Maximum number of items to return. The maximum is 1000.

Response

bulk_id
string
The ID of the bulk job.
offset
integer
The returned page offset.
limit
integer
The returned page limit.
total_items
integer
Total number of submitted items.
items
array
Item metadata. Each item includes index, optional id, task_id, stage, and optional error.
Example Response
{
  "bulk_id": "blk_123",
  "offset": 0,
  "limit": 100,
  "total_items": 2,
  "items": [
    {
      "index": 0,
      "id": "row-001",
      "task_id": "123e4567-e89b-12d3-a456-426614174000",
      "stage": "STAGE_SUCCESS",
      "error": null
    },
    {
      "index": 1,
      "id": "row-002",
      "task_id": null,
      "stage": "STAGE_FAILED",
      "error": "Text must contain at least one valid token"
    }
  ]
}

GET /bulk//results

Fetch paginated results for a bulk job.
GET https://text.external-api.pangram.com/bulk/{bulk_id}/results?offset=0&limit=100

Request

bulk_id
string
required
The bulk job ID returned by POST /bulk.
offset
integer
default:"0"
Zero-based item offset.
limit
integer
default:"100"
Maximum number of items to return. The maximum is 1000.

Response

bulk_id
string
The ID of the bulk job.
offset
integer
The returned page offset.
limit
integer
The returned page limit.
total_items
integer
Total number of submitted items.
items
array
Result items for successful or in-progress work. Successful completed items include result; in-progress items have result: null.
failed_items
array
Failed item metadata for the requested page.
Example Response
{
  "bulk_id": "blk_123",
  "offset": 0,
  "limit": 100,
  "total_items": 2,
  "items": [
    {
      "index": 0,
      "id": "row-001",
      "task_id": "123e4567-e89b-12d3-a456-426614174000",
      "stage": "STAGE_SUCCESS",
      "error": null,
      "result": {
        "text": "First text to analyze",
        "version": "3.3",
        "prediction": "We believe this is human-written",
        "prediction_short": "Human",
        "fraction_ai": 0.0,
        "fraction_ai_assisted": 0.0,
        "fraction_human": 1.0,
        "headline": "Human Written",
        "num_ai_segments": 0,
        "num_ai_assisted_segments": 0,
        "num_human_segments": 1,
        "windows": []
      }
    }
  ],
  "failed_items": [
    {
      "index": 1,
      "id": "row-002",
      "task_id": null,
      "stage": "STAGE_FAILED",
      "error": "Text must contain at least one valid token"
    }
  ]
}

Errors

Status CodeDescription
401 UnauthorizedThe x-api-key is missing or invalid.
402 Payment RequiredThe account has insufficient credits.
403 ForbiddenThe API key does not own the requested bulk job.
404 Not FoundThe requested bulk job does not exist.
413 Payload Too LargeThe bulk request exceeds the maximum billable units.
422 Unprocessable EntityThe request is empty, contains both items and text, includes duplicate item IDs, or otherwise fails validation.
500 Internal Server ErrorThere was an error processing the request.