DeepSeek: Deepseek V4.1 Flash (Batch) API Reference

Infron Batch runs inference offline: submit multiple requests, receive a job ID, and download the results when processing finishes. Each job uses one model.

Use this API for offline generation, evaluation, summarization, and other workloads that do not need an immediate response. Select a model with Batch support.

The workflow is: create a job → query its status → download the result file → process each result by custom_id.

Contents

  1. Authentication and endpoints
  2. Create a job
  3. Retrieve a job
  4. Download and read results
  5. List and cancel jobs
  6. Limits and errors

1. Authentication and endpoints

Create an API key in the Infron dashboard. Gateway requests use Authorization: Bearer <INFRON_API_KEY>; JSON request bodies use Content-Type: application/json.

OperationMethodFull endpoint URLSuccess
Create a jobPOSThttps://llm.onerouter.pro/v1/batch202
List jobsGEThttps://llm.onerouter.pro/v1/batch200
Retrieve a jobGEThttps://llm.onerouter.pro/v1/batch/{id}200
Cancel a jobPOSThttps://llm.onerouter.pro/v1/batch/{id}/cancel200

Use /v1/batch for Batch API requests.

Install the Python dependency and set your API key:

python3 -m pip install requests
export INFRON_API_KEY='YOUR_API_KEY'

Run the following Python snippets in order, sharing this configuration:

import os
import requests

BASE_URL = "https://llm.onerouter.pro"
MODEL = "infron-batch/deepseek-v4.1-flash"
HEADERS = {
    "Authorization": f"Bearer {os.environ['INFRON_API_KEY']}",
    "Content-Type": "application/json",
    "User-Agent": "infron-batch-guide/1.0",
    "Accept": "application/json",
    "Connection": "keep-alive",
}

2. Create a job

POST /v1/batch

Set the model at the top level and put individual requests in requests. Use a unique custom_id to identify each business record. Each body uses Chat Completions format. This example submits 11 explicitly listed requests, with IDs q1 through q11.

FieldRequiredDescription
modelYesModel ID for the entire job: infron-batch/deepseek-v4.1-flash
requestsYes for this formatNonempty array of individual requests
requests[].custom_idRecommendedUnique within the job, up to 128 bytes; omitted IDs are generated as row-0, row-1, etc.
requests[].bodyYesChat Completions request object containing messages and supported generation parameters

With requests, keep only model and requests at the top level. A body.model, if provided, must match the job model. Do not send a provider routing field or use the legacy requests[].params structure. Model parameters such as tools, structured output, and media inputs require support from the selected model.

payload = {
    "model": MODEL,
    "requests": [
        {
            "custom_id": "q1",
            "body": {
                "messages": [{"role": "user", "content": "Describe Beijing in one sentence."}],
            },
        },
        {
            "custom_id": "q2",
            "body": {
                "messages": [{"role": "user", "content": "Describe Shanghai in one sentence."}],
            },
        },
        {
            "custom_id": "q3",
            "body": {
                "messages": [{"role": "user", "content": "Describe Shanghai in one sentence."}],
            },
        },
        {
            "custom_id": "q4",
            "body": {
                "messages": [{"role": "user", "content": "Describe Shanghai in one sentence."}],
            },
        },
        {
            "custom_id": "q5",
            "body": {
                "messages": [{"role": "user", "content": "Describe Shanghai in one sentence."}],
            },
        },
        {
            "custom_id": "q6",
            "body": {
                "messages": [{"role": "user", "content": "Describe Shanghai in one sentence."}],
            },
        },
        {
            "custom_id": "q7",
            "body": {
                "messages": [{"role": "user", "content": "Describe Shanghai in one sentence."}],
            },
        },
        {
            "custom_id": "q8",
            "body": {
                "messages": [{"role": "user", "content": "Describe Shanghai in one sentence."}],
            },
        },
        {
            "custom_id": "q9",
            "body": {
                "messages": [{"role": "user", "content": "Describe Shanghai in one sentence."}],
            },
        },
        {
            "custom_id": "q10",
            "body": {
                "messages": [{"role": "user", "content": "Describe Shanghai in one sentence."}],
            },
        },
        {
            "custom_id": "q11",
            "body": {
                "messages": [{"role": "user", "content": "Describe Shanghai in one sentence."}],
            },
        },
    ],
}

response = requests.post(
    f"{BASE_URL}/v1/batch",
    headers=HEADERS,
    json=payload,
    timeout=180,
)
response.raise_for_status()
batch_id = response.json()["id"]
print(batch_id)

Success returns 202 Accepted and a complete job object. The following example shows all fields returned for this queued job. IDs, sizes, quota amounts, and timestamps are illustrative; use the values returned by your request:

{
  "id": "batch_f32a93f000de4d959c907f167a94137b",
  "model": "infron-batch/deepseek-v4.1-flash",
  "status": "queued",
  "input_bytes": 1036,
  "request_count": 11,
  "completed_count": 0,
  "failed_count": 0,
  "estimated_quota": 4994,
  "charged_quota": 0,
  "expires_at": 1791535183,
  "result_bytes": 0,
  "s3_usage": {
    "put": 1,
    "put_bytes": 1036
  },
  "created_at": 1791448782,
  "updated_at": 1791448800
}

Put generation parameters such as temperature and max_tokens inside each body. A job uses one model and produces non-streaming results.

3. Retrieve a job

GET /v1/batch/{id}

response = requests.get(
    f"{BASE_URL}/v1/batch/{batch_id}",
    headers=HEADERS,
    timeout=30,
)
response.raise_for_status()
job = response.json()
print(job)

Complete response after processing finishes. The signed URL contains illustrative signature placeholders and cannot be used for a download:

{
  "id": "batch_f32a93f000de4d959c907f167a94137b",
  "model": "infron-batch/deepseek-v4.1-flash",
  "status": "completed",
  "input_bytes": 1036,
  "request_count": 11,
  "completed_count": 11,
  "failed_count": 0,
  "estimated_quota": 4994,
  "charged_quota": 721,
  "expires_at": 1791535183,
  "result_bytes": 3005,
  "s3_usage": {
    "get": 14,
    "put": 13,
    "get_bytes": 19423,
    "put_bytes": 20356
  },
  "created_at": 1791448782,
  "updated_at": 1791448849,
  "completed_at": 1791448843,
  "result_url": "https://infron-batch-prod.s3.us-west-2.amazonaws.com/infron-batch/batch_f32a93f000de4d959c907f167a94137b/output-1.jsonl.gz?X-Amz-Algorithm=...&X-Amz-Expires=900&X-Amz-Signature=<SIGNATURE_OMITTED>",
  "result_expires_in": 900
}
FieldMeaning
id, modelJob ID and selected model
statusqueued: waiting or executing; finalizing: assembling results; completed, cancelled, and failed: terminal states
input_bytesInput size in bytes
request_countTotal requests
completed_count, failed_countSuccessful and unsuccessful counts; unsuccessful includes failures, cancellations, and expirations
estimated_quota, charged_quotaEstimated and settled quota; the estimate is not a spending cap
expires_atQueue deadline in Unix seconds, not result-file expiration
result_bytesCompressed result-file size in bytes
s3_usageStorage statistics: get / put count operations; get_bytes / put_bytes count bytes
created_at, updated_at, completed_atCreation, update, and termination times in Unix seconds; completed_at is absent before termination
result_url, result_expires_inIndividual-job query download URL and lifetime in seconds, when results are ready and within retention
cancelled_atCancellation-request time in Unix seconds, present after cancellation is requested
error_code, error_messageJob-level error code and message, when present

Poll about every 10 seconds. On 429, wait according to Retry-After. completed means the job ended, not that every request succeeded. For failed, inspect error_code and error_message.

4. Download and read results

The output is gzip-compressed JSONL with one record per request. Match records to your input using custom_id.

import gzip
import json

# Use the query response's URL. Do not attach the Infron API key to storage calls.
with requests.get(
    job["result_url"],
    headers={
        "User-Agent": "infron-batch-guide/1.0",
        "Accept": "application/octet-stream",
        "Accept-Encoding": "identity",
        "Connection": "keep-alive",
    },
    stream=True,
    timeout=180,
) as response:
    response.raise_for_status()
    with open("output.jsonl.gz", "wb") as output:
        for chunk in response.raw.stream(1024 * 1024, decode_content=False):
            output.write(chunk)

with gzip.open("output.jsonl.gz", "rt", encoding="utf-8") as result_file:
    for line in result_file:
        item = json.loads(line)
        if item.get("error"):
            print(item["custom_id"], "Error:", item["error"])
        else:
            print(item["custom_id"], item["response"]["body"])

Successful result example. All values are illustrative; response content and usage details vary by model:

{
  "completed_at": 1759900000,
  "index": 0,
  "custom_id": "q1",
  "response": {
    "status_code": 200,
    "body": {
      "choices": [
        {
          "index": 0,
          "message": {
            "role": "assistant",
            "content": "Beijing is the capital of China."
          },
          "finish_reason": "stop"
        }
      ],
      "usage": {
        "prompt_tokens": 16,
        "completion_tokens": 8,
        "total_tokens": 24
      },
      "request_id": "request_example",
      "service_tier": "standard"
    }
  },
  "error": null
}

Example failed record:

{
  "index": 1,
  "custom_id": "q2",
  "response": null,
  "error": {
    "code": "insufficient_quota",
    "message": "Batch request failed: insufficient_quota"
  }
}

Download URLs default to 15 minutes of validity. Query the job again if the URL expires. Result files default to 7 days of retention; production configuration may vary, so download promptly.

5. List and cancel jobs

List jobs

GET /v1/batch

response = requests.get(f"{BASE_URL}/v1/batch", headers=HEADERS, timeout=30)
response.raise_for_status()
page = response.json()

# Pass the returned next_cursor unchanged to after.
if page.get("next_cursor"):
    response = requests.get(
        f"{BASE_URL}/v1/batch",
        headers=HEADERS,
        params={"after": page["next_cursor"]},
        timeout=30,
    )
    response.raise_for_status()

Complete list response example:

{
  "object": "list",
  "data": [
    {
      "id": "batch_f32a93f000de4d959c907f167a94137b",
      "model": "infron-batch/deepseek-v4.1-flash",
      "status": "completed",
      "input_bytes": 1036,
      "request_count": 11,
      "completed_count": 11,
      "failed_count": 0,
      "estimated_quota": 4994,
      "charged_quota": 721,
      "expires_at": 1791535183,
      "result_bytes": 3005,
      "s3_usage": {
        "get": 14,
        "put": 13,
        "get_bytes": 19423,
        "put_bytes": 20356
      },
      "created_at": 1791448782,
      "updated_at": 1791448849,
      "completed_at": 1791448843
    }
  ],
  "next_cursor": ""
}

Each page contains up to 50 jobs. An empty next_cursor means there is no next page. Do not substitute a batch_... ID for the cursor. List responses do not include download URLs; query an individual job to obtain result_url.

Cancel a job

POST /v1/batch/{id}/cancel

response = requests.post(
    f"{BASE_URL}/v1/batch/{batch_id}/cancel",
    headers=HEADERS,
    timeout=30,
)
response.raise_for_status()
print(response.json())

Success returns 200 and a complete job object. This cancellation response illustrates the structure; the cancellation timestamp is an example value:

{
  "id": "batch_f32a93f000de4d959c907f167a94137b",
  "model": "infron-batch/deepseek-v4.1-flash",
  "status": "cancelling",
  "input_bytes": 1036,
  "request_count": 11,
  "completed_count": 0,
  "failed_count": 0,
  "estimated_quota": 4994,
  "charged_quota": 0,
  "expires_at": 1791535183,
  "result_bytes": 0,
  "s3_usage": {
    "put": 1,
    "put_bytes": 1036
  },
  "created_at": 1791448782,
  "updated_at": 1791448801,
  "cancelled_at": 1791448801
}

Continue polling until a terminal state. Repeated cancellation is safe; ended jobs return their current state. Completed requests may retain results and incur charges.

6. Limits and errors

These are implementation defaults; effective service settings can differ.

LimitDefault
Requests per job10,000
Inline request body64 MiB
Result download URL lifetime900 seconds; use result_expires_in
Result retention7 days

1 MiB = 1,048,576 bytes. Gateway JSON submissions must not use Content-Encoding; result files are gzip compressed.

HTTP failures contain an error object, for example:

{
  "error": {
    "message": "Batch is temporarily unavailable",
    "type": "infron_ai_error"
  }
}
HTTP statusAction
400Check request fields, model access, and the returned message
401 / 403Check your API key, model permissions, and job ownership
409Check conflicting submission state
413Split the input into smaller jobs; remove request compression
415Send the JSON request body with Content-Type: application/json
429Honor Retry-After and reduce request rate or active workload
503Service temporarily unavailable; retry queries with backoff

A job can fail after acceptance. Read error_code and error_message for job-level failures, and error.code and error.message in each result line for request-level failures. A successful creation response does not guarantee that every request will succeed.

See the Batch quickstart for the public usage guide.