# Mistral: OCR Upload

Upload a file directly via multipart/form-data and run Mistral OCR on it — no hosting step needed. Same pricing as POST /ocr: $0.01 per page, $0.02 per page with annotation options. Fields: file (PDF or PNG/JPEG/AVIF image, max 50 MB), pages (optional, a range string like "0,2-4" or JSON array string like "[0,1]"; omit to process the whole PDF, counted from the uploaded bytes, max 25 MB for whole-document pricing), and options (optional JSON object string with the same option fields as POST /ocr). Images always count as one page. The body is validated before charging and upstream failures are refunded automatically.

- Method: POST
- Path: `/ocr/upload`
- Price: $0.01000000
- Content-Type: multipart/form-data

## Input Schema

```json
{
  "type": "object",
  "properties": {
    "file": {
      "description": "The document (PDF) or image (PNG, JPEG, AVIF) to process"
    },
    "pages": {
      "anyOf": [
        {
          "anyOf": [
            {
              "type": "array",
              "items": {
                "type": "integer",
                "minimum": 0
              },
              "maxItems": 1000
            },
            {
              "type": "string",
              "pattern": "^\\d+(-\\d+)?(,\\d+(-\\d+)?)*$"
            }
          ]
        },
        {
          "type": "null"
        }
      ],
      "description": "Same semantics as POST /ocr pages, as a form field: a range string like \"0,2-4\" or a JSON array string like \"[0, 1, 2]\". Omit to process the whole document (PDF page count is determined from the uploaded bytes; images always count as one page)."
    },
    "options": {
      "type": "object",
      "properties": {
        "model": {
          "type": [
            "string",
            "null"
          ],
          "description": "Mistral OCR model id. Defaults to mistral-ocr-latest."
        },
        "include_image_base64": {
          "type": [
            "boolean",
            "null"
          ],
          "description": "Return base64 image data for images extracted from the document"
        },
        "image_limit": {
          "anyOf": [
            {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            {
              "type": "null"
            }
          ],
          "description": "Maximum number of images to extract"
        },
        "image_min_size": {
          "anyOf": [
            {
              "type": "integer",
              "exclusiveMinimum": 0
            },
            {
              "type": "null"
            }
          ],
          "description": "Minimum height and width, in pixels, for an image to be extracted"
        },
        "bbox_annotation_format": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "text",
                    "json_object",
                    "json_schema"
                  ]
                }
              },
              "required": [
                "type"
              ],
              "additionalProperties": true
            },
            {
              "type": "null"
            }
          ],
          "description": "Structured format for per-image bbox annotations (upstream accepts json_schema only). Setting this prices the call at the annotation rate."
        },
        "document_annotation_format": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "text",
                    "json_object",
                    "json_schema"
                  ]
                }
              },
              "required": [
                "type"
              ],
              "additionalProperties": true
            },
            {
              "type": "null"
            }
          ],
          "description": "Structured format for a whole-document annotation (upstream accepts json_schema only). Setting this prices the call at the annotation rate."
        },
        "document_annotation_prompt": {
          "type": [
            "string",
            "null"
          ],
          "description": "Optional prompt guiding structured extraction over the whole document. Requires document_annotation_format. Setting this prices the call at the annotation rate."
        },
        "table_format": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "markdown",
                "html"
              ]
            },
            {
              "type": "null"
            }
          ],
          "description": "Output format for extracted tables"
        },
        "extract_header": {
          "type": "boolean",
          "description": "Extract page headers separately"
        },
        "extract_footer": {
          "type": "boolean",
          "description": "Extract page footers separately"
        },
        "confidence_scores_granularity": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "word",
                "page"
              ]
            },
            {
              "type": "null"
            }
          ],
          "description": "Granularity for OCR confidence scores: \"word\" (per-word) or \"page\" (aggregate only). Omit for no confidence scores."
        }
      },
      "additionalProperties": false,
      "description": "Optional JSON object string with the same option fields as POST /ocr (model, include_image_base64, image_limit, image_min_size, bbox_annotation_format, document_annotation_format, document_annotation_prompt, table_format, extract_header, extract_footer, confidence_scores_granularity). Annotation options price the call at the annotation rate."
    }
  },
  "required": [
    "file"
  ],
  "additionalProperties": false
}
```

## Output Schema

```json
{
  "type": "object",
  "properties": {
    "pages": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer"
          },
          "markdown": {
            "type": "string"
          },
          "images": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                }
              },
              "required": [
                "id"
              ],
              "additionalProperties": true
            }
          },
          "dimensions": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "dpi": {
                    "type": "number"
                  },
                  "height": {
                    "type": "number"
                  },
                  "width": {
                    "type": "number"
                  }
                },
                "required": [
                  "dpi",
                  "height",
                  "width"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "index",
          "markdown"
        ],
        "additionalProperties": true
      }
    },
    "model": {
      "type": "string"
    },
    "document_annotation": {
      "type": [
        "string",
        "null"
      ]
    },
    "usage_info": {
      "type": "object",
      "properties": {
        "pages_processed": {
          "type": "integer"
        },
        "doc_size_bytes": {
          "type": [
            "number",
            "null"
          ]
        }
      },
      "required": [
        "pages_processed"
      ],
      "additionalProperties": true
    }
  },
  "required": [
    "pages",
    "model",
    "usage_info"
  ],
  "additionalProperties": true
}
```

## Networks

| Name | CAIP-2 | Chain ID / Cluster | Protocols |
|---|---|---|---|
| Tempo Mainnet | `eip155:4217` | 4217 | mpp |
| Solana Mainnet | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | mainnet-beta | mpp, x402 |
| Base | `eip155:8453` | 8453 | x402 |

## Accepted Currencies

| Symbol | Name | Decimals | Network | Address | Protocols |
|---|---|---|---|---|---|
| USDC.e | Bridged USDC (Stargate) | 6 | `eip155:4217` | `0x20c000000000000000000000b9537d11c60e8b50` | mpp |
| USDC | USD Coin (Solana) | 6 | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` | mpp, x402 |
| USDC | USD Coin | 6 | `eip155:8453` | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | x402 |

## How to invoke

Payment is handled automatically — the client signs the 402 challenge and retries.

### TypeScript — Tempo (mppx + fetch) ([docs](https://mpp.dev/sdk/typescript/client))

```ts
import { privateKeyToAccount } from 'viem/accounts'
import { Mppx, tempo } from 'mppx/client'

Mppx.create({
  methods: [tempo({ account: privateKeyToAccount('0x...') })],
})

const form = new FormData()
form.append('file', /* File from <input> or Blob */, 'file.bin')
form.append('pages', '[0]')
form.append('options', '{"model":"","include_image_base64":false,"image_limit":0}')

const res = await fetch('https://mistral.payweave.services/ocr/upload', {
  method: 'POST',
  body: form,
})
const data = await res.json()
```

### TypeScript — Solana (@solana/mpp + fetch) ([docs](https://github.com/solana-foundation/mpp-sdk))

```ts
import { createKeyPairSignerFromBytes } from '@solana/kit'
import { Mppx } from 'mppx/client'
import { solana } from '@solana/mpp/client'

const signer = await createKeyPairSignerFromBytes(/* 64-byte secret key */)
Mppx.create({ methods: [solana({ signer })] })

const form = new FormData()
form.append('file', /* File from <input> or Blob */, 'file.bin')
form.append('pages', '[0]')
form.append('options', '{"model":"","include_image_base64":false,"image_limit":0}')

const res = await fetch('https://mistral.payweave.services/ocr/upload', {
  method: 'POST',
  body: form,
})
const data = await res.json()
```

### mppx CLI ([docs](https://mpp.dev/sdk/typescript/cli))

```sh
npx mppx "https://mistral.payweave.services/ocr/upload" -X POST -F "file=@./file.bin" -F "pages=[0]" -F "options={"model":"","include_image_base64":false,"image_limit":0}"
```

### Tempo Wallet ([docs](https://docs.tempo.xyz/cli/wallet))

```sh
tempo request "https://mistral.payweave.services/ocr/upload" -X POST -F "file=@./file.bin" -F "pages=[0]" -F "options={"model":"","include_image_base64":false,"image_limit":0}"
```

### pay.sh — Solana Foundation ([docs](https://pay.sh))

```sh
pay --mainnet curl "https://mistral.payweave.services/ocr/upload" -X POST -F "file=@./file.bin" -F "pages=[0]" -F "options={"model":"","include_image_base64":false,"image_limit":0}"
```

### x402 — Base / Solana USDC (x402-fetch) ([docs](https://x402.org))

```ts
import { wrapFetchWithPayment } from 'x402-fetch'
import { privateKeyToAccount } from 'viem/accounts'

const account = privateKeyToAccount('0x...') // Base USDC wallet
const fetchWithPay = wrapFetchWithPayment(fetch, account)

const form = new FormData()
form.append('file', /* File from <input> or Blob */, 'file.bin')
form.append('pages', '[0]')
form.append('options', '{"model":"","include_image_base64":false,"image_limit":0}')

const res = await fetchWithPay('https://mistral.payweave.services/ocr/upload', {
  method: 'POST',
  body: form,
})
const data = await res.json()
```
