> For clean Markdown of any page, append `.md` to the page URL.
> For a complete documentation index, see https://docs.sarvam.ai/llms.txt.
> For full documentation content in one file, see https://docs.sarvam.ai/llms-full.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.sarvam.ai/_mcp/server.

# API reference — Sarvam Vision

> The InvokeEndpoint request and response contract for a self-hosted Sarvam Vision endpoint on SageMaker: content types, custom-attribute options, limits, the JSON response envelope, and error codes.

The contract for invoking a self-hosted **Sarvam Vision** endpoint. You send the raw document bytes as the request body and pass options as **custom attributes**.

## `InvokeEndpoint`

| Field            | Value                                                                     |
| ---------------- | ------------------------------------------------------------------------- |
| **Operation**    | `sagemaker-runtime:InvokeEndpoint` (sync) · `InvokeEndpointAsync` (async) |
| **Request body** | the raw document bytes                                                    |
| **Content-Type** | must match the bytes (magic-byte verified) — see below                    |
| **Accept**       | `application/json` (default) or `application/zip` (bundle)                |

### Supported content types

| Content-Type                                          | File type     |
| ----------------------------------------------------- | ------------- |
| `application/pdf`                                     | PDF           |
| `image/png`                                           | PNG image     |
| `image/jpeg` (or `image/jpg`)                         | JPEG image    |
| `application/zip` (or `application/x-zip-compressed`) | ZIP of images |

An omitted type, `application/octet-stream`, or `text/plain` is treated as PDF and the bytes are sniffed. A `Content-Type` that doesn't match the bytes is rejected with `400 INVALID_INPUT`.

**TIFF, WebP, BMP, and GIF are not supported** — they're rejected with `415 UNSUPPORTED_MEDIA_TYPE`. Convert to PNG, JPEG, or PDF first.

### Limits

| Limit                          | Value            | Error if exceeded                                     |
| ------------------------------ | ---------------- | ----------------------------------------------------- |
| File size (container hard cap) | 200 MB           | `413 PAYLOAD_TOO_LARGE`                               |
| File size (sync endpoint)      | \~6 MB (AWS cap) | AWS-side rejection before the container               |
| File size (async endpoint)     | 50 MB            | AWS-side / job failure                                |
| Pages per sync request         | 5 (recommended)  | risk of the 60 s `InvokeEndpoint` timeout beyond this |
| Pages per document (hard cap)  | 500              | `413 PAYLOAD_TOO_LARGE`                               |

### Options — custom attributes

Pass options in the `X-Amzn-SageMaker-Custom-Attributes` header (sync) or the `CustomAttributes` parameter (async), as `key=value,key=value` or a JSON object.

| Attribute       | Values                                                                                                                                                                                                               | Default                   | Notes                                                        |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | ------------------------------------------------------------ |
| `language`      | `hi-IN`, `en-IN`, `bn-IN`, `gu-IN`, `kn-IN`, `ml-IN`, `mr-IN`, `or-IN`, `pa-IN`, `ta-IN`, `te-IN`, `ur-IN`, `as-IN`, `bodo-IN`, `doi-IN`, `ks-IN`, `kok-IN`, `mai-IN`, `mni-IN`, `ne-IN`, `sa-IN`, `sat-IN`, `sd-IN` | `hi-IN`                   | case-insensitive; anything else → `400 UNSUPPORTED_LANGUAGE` |
| `output_format` | `md` \| `html` \| `json`                                                                                                                                                                                             | `md`                      | `json` adds structured `result.pages[]`                      |
| `output`        | `text` \| `zip`                                                                                                                                                                                                      | `text`                    | response representation (see below)                          |
| `filename`      | e.g. `invoice.pdf`                                                                                                                                                                                                   | derived from Content-Type | appears in logs and results                                  |

## Response

The default response is a **JSON envelope** (`Content-Type: application/json`):

```json
{
  "schema_version": "1.0",
  "status": "completed",
  "job_id": "20260724_...",
  "result": {
    "format": "md",
    "text": "…merged document text…",
    "summary": { "total": 1, "succeeded": 1, "failed": 0 }
  },
  "warnings": []
}
```

With `output_format=json`, `result.pages[]` is added — per page `page_num`, `width`, `height`, `unit`, `origin`, and `blocks[]` with `id`, `type` (layout tag), `reading_order`, `bbox`, `text`, `layout_confidence`, and `ocr_confidence`.

Set `output=zip` (or send `Accept: application/zip`) to get a **zip bundle** instead: the merged document at the root (`{name}.md` / `.html`, always present) plus `metadata/page_NNN.json` per page.

The response header `X-Amzn-SageMaker-Custom-Attributes` echoes `job_id`, `schema_version`, and `api_version`. **Log the `job_id`** — support can trace any request with it.

The sync endpoint is for **small documents (up to 5 pages) at low concurrency**. It must finish inside AWS's **60-second** timeout, so keep sync requests to **5 pages or fewer** and, on the recommended single-GPU instance (`ml.g6e.xlarge`), only **1–2 documents in flight** or requests will time out. For anything larger than 5 pages, higher concurrency, or bulk work, use an **async** endpoint or **batch transform** — see [Deploy Sarvam Vision](/api/self-hosted/sagemaker/deploy-vision#prefer-async-for-documents).

## Errors

Every non-2xx is delivered as **HTTP 424** through `InvokeEndpoint` — branch on `OriginalStatusCode` plus `error.code` / `error.detail_code`, never on the HTTP status. See [Error handling on SageMaker](/api/self-hosted/sagemaker/errors) for the collapse rule and the shared envelope.

Errors always come back as the same JSON envelope — never HTML, never a bare string. The **error** envelope uses `schema_version: "2.0"` (a successful response is `"1.0"` — a known inconsistency). `error` carries `code` (the class), `detail_code` (the specific reason), `message`, `retryable`, and `request_id` — plus `retry_after` on an overload `503`:

```json
{
  "schema_version": "2.0",
  "status": "error",
  "error": {
    "code": "invalid_request_error",
    "detail_code": "UNSUPPORTED_MEDIA_TYPE",
    "message": "content type 'image/tiff' is not supported; supported: application/pdf, image/png, image/jpeg, application/zip …",
    "retryable": false,
    "request_id": "20260729_…"
  }
}
```

### Client errors — fix the request, don't retry

`error.code` is `invalid_request_error` and `retryable` is `false`; the `error.detail_code` names the specific reason.

| `OriginalStatusCode` | `error.detail_code`      | Meaning                                                                                               |
| :------------------: | ------------------------ | ----------------------------------------------------------------------------------------------------- |
|          400         | `INVALID_INPUT`          | empty body, corrupt/truncated or password-protected PDF, bytes don't match `Content-Type`, zero pages |
|          400         | `UNSUPPORTED_LANGUAGE`   | `language` not in the list above (e.g. `"unsupported language 'fr-FR'"`)                              |
|          413         | `PAYLOAD_TOO_LARGE`      | over 200 MB or over 500 pages — split the document                                                    |
|          415         | `UNSUPPORTED_MEDIA_TYPE` | TIFF/WebP/BMP/GIF or another unsupported type — convert to PDF/PNG/JPEG                               |

### Overload — back off and retry

`error.code` is `service_overloaded`, `retryable` is `true`, and the body carries `retry_after` (seconds). The container sheds immediately rather than queueing.

```json
{
  "schema_version": "2.0",
  "status": "error",
  "error": {
    "code": "service_overloaded",
    "detail_code": "SERVICE_UNAVAILABLE",
    "message": "server at capacity, retry later",
    "retryable": true,
    "request_id": "20260729_…",
    "retry_after": 5
  }
}
```

### Other server errors — retryable

`retryable` is `true`; back off and retry, and report `error.request_id` if one file fails persistently.

| `OriginalStatusCode` | `error.detail_code`    | Meaning                                                                     |
| :------------------: | ---------------------- | --------------------------------------------------------------------------- |
|          500         | `INTERNAL_ERROR`       | processing failed (rare) — resubmit                                         |
|          503         | `INSUFFICIENT_STORAGE` | instance disk guard tripped — sustained → raise instance volume size        |
|          504         | `PROCESSING_TIMEOUT`   | document exceeded the per-job wait budget — raise timeouts (async) or split |

Rule of thumb: **`4xx` = fix the request; `5xx` = retry with backoff.** The `retryable` flag in every error body encodes exactly this.

#### [Deploy a Sarvam Vision endpoint](/api/self-hosted/sagemaker/deploy-vision)

Step-by-step real-time, async, and batch deployment with boto3.