# Upload a file

Upload a file and keep the returned asset ID for extraction, preview, or document analysis.

Documentation index: https://webcite.co/llms.txt
Canonical page: https://webcite.co/api-docs/upload
API origin: https://api.webcite.co
Authentication: x-api-key header. Keep keys on your server.

## When to use it

Send `multipart/form-data` with one `file` field. Let the HTTP client set the multipart boundary. Do not send a local file path as JSON.

The hosted MCP upload tool uses filename and base64 content with a 20 MB limit; that is a separate transport from this HTTP endpoint.

## Request

POST /api/v1/upload

1 credit for a successful upload.

### curl

```curl
curl --fail-with-body -X POST 'https://api.webcite.co/api/v1/upload' \
  -H "x-api-key: $WEBCITE_API_KEY" \
  -F "file=@./report.pdf"
```

### Node.js

```javascript
import { readFile } from "node:fs/promises";

const form = new FormData();
form.append("file", new Blob([await readFile("./report.pdf")]), "report.pdf");
const response = await fetch("https://api.webcite.co/api/v1/upload", {
  method: "POST",
  headers: {
    "x-api-key": process.env.WEBCITE_API_KEY,
  },
  body: form,
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());
```

### python

```python
import os
import requests

with open("report.pdf", "rb") as file:
    response = requests.post(
        "https://api.webcite.co/api/v1/upload",
        headers={"x-api-key": os.environ["WEBCITE_API_KEY"]},
        files={"file": file},
        timeout=(10, 300),
    )
response.raise_for_status()
print(response.json())
```

## Response

Returns the storage upload result. Keep the asset ID from the response. The public OpenAPI does not yet provide a complete upload response schema. Upload billing is recorded separately and does not add a top-level usage object.

## Errors

An unsuccessful upload uses a non-success HTTP status. A 503 can mean private evidence storage is unavailable. Check the response before starting extraction; an upload failure does not provide a usable asset.

## OpenAPI operation

```json
{
  "path": "/api/v1/upload",
  "method": "POST",
  "operation": {
    "description": "Upload a file to storage for use in verification context.",
    "operationId": "ApiV1Controller_uploadFile",
    "parameters": [],
    "requestBody": {
      "content": {
        "multipart/form-data": {
          "schema": {
            "properties": {
              "file": {
                "description": "File to upload",
                "format": "binary",
                "type": "string"
              }
            },
            "required": [
              "file"
            ],
            "type": "object"
          }
        }
      },
      "required": true
    },
    "responses": {
      "200": {
        "description": "File uploaded successfully"
      },
      "401": {
        "description": "Unauthorized - API key required"
      },
      "503": {
        "description": "Private evidence storage required: set EVIDENCE_STORAGE_ROOT or EVIDENCE_BUCKET_NAME (I1; never public-first)"
      }
    },
    "security": [
      {
        "x-api-key": []
      },
      {
        "bearer": []
      }
    ],
    "summary": "Upload a file",
    "tags": [
      "Public API"
    ]
  },
  "schemas": {}
}
```
