---
title: Files and Uploads | RunAPI
description: Create, list, download, and delete persistent Files, or assemble a File
  from multipart Upload Parts.
url: https://runapi.ai/docs/resources/files.md
canonical: https://runapi.ai/docs/resources/files
locale: en
---

> HTML version: https://runapi.ai/docs/resources/files
> Site index for agents: https://runapi.ai/llms.txt

# Files and Uploads

The Files API stores immutable, Account-scoped `user_data`, `batch`, and
`batch_output` Files and returns stable File IDs instead of storage
URLs. Use a File ID when an API accepts a reusable File resource, and
use the content endpoint when your application needs the exact stored
bytes.

## Choose an upload flow

* Use `POST /v1/files` to create a File from one multipart request. A
  completed `user_data` File can contain up to `52,428,800` bytes.
* Use `POST /v1/files` with `purpose=batch` for a direct batch input up
  to `95,000,000` bytes.
* Use the Uploads API to send Parts before composing the final File.
  Each Part can contain up to `67,108,864` bytes; a completed
  `user_data` File is limited to `52,428,800` bytes and a completed
  `batch` File to `209,715,200` bytes. An unfinished Upload expires
  after one hour.
* Active Files and in-progress reservations share an Account storage
  limit of `5,368,709,120` bytes.

`POST /api/v1/files` is a separate temporary upload flow that returns a
temporary URL. SDK `files.create` and CLI `runapi files create` continue
to use that flow. Use `files.createFile` or `files.create_file`, or CLI
`runapi files create-file`, when you need a persistent File object.

## Authenticate a request

Files and Uploads use a standard RunAPI API key. Send it as a Bearer
token:

```shell
export RUNAPI_API_KEY="runapi_..."
```

Every File, Upload, Part, and content request is scoped to the
authenticated Account. An ID owned by another Account returns `404`
without revealing whether that resource exists.

## Create a File with a compatible client

Point an OpenAI-compatible client at the RunAPI `/v1` base URL. No
RunAPI-specific request fields are required:

```python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["RUNAPI_API_KEY"],
    base_url="https://runapi.ai/v1",
)

with open("knowledge.pdf", "rb") as source:
    file = client.files.create(file=source, purpose="user_data")

metadata = client.files.retrieve(file.id)
client.files.content(file.id).write_to_file("knowledge-copy.pdf")
client.files.delete(file.id)
```

The returned File never contains a storage identifier, backend, or
bearer URL.

## API Reference

* [Persistent Files API](https://runapi.ai/docs/api/protocol/files.md) covers the File
  object and create, list, retrieve, content, and delete operations.
* [Uploads API](https://runapi.ai/docs/api/protocol/uploads.md) covers the Upload object and
  create, add-part, complete, and cancel operations.
* [Batches API](https://runapi.ai/docs/api/protocol/batches.md) covers Moderation Batch
  creation, status, listing, and cancellation.

## Run Moderation in a Batch

Create a JSONL input File with one moderation request per line. Each
line uses the compatible Batch shape: `custom_id`, `method`, `url` set
to `/v1/moderations`, and a `body` containing the moderation model and
input. A `purpose=batch` File can contain up to 50,000 requests. The
Batch capability currently accepts the `/v1/moderations` endpoint with a
`24h` completion window.

```shell
INPUT_FILE_ID=$(curl -sS https://runapi.ai/v1/files \
  -H "Authorization: Bearer $RUNAPI_API_KEY" \
  -F purpose=batch \
  -F file=@moderation.jsonl | jq -r .id)

BATCH_ID=$(curl -sS https://runapi.ai/v1/batches \
  -H "Authorization: Bearer $RUNAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg file "$INPUT_FILE_ID" '{input_file_id:$file,endpoint:"/v1/moderations",completion_window:"24h"}')" \
  | jq -r .id)

curl -sS "https://runapi.ai/v1/batches/$BATCH_ID" \
  -H "Authorization: Bearer $RUNAPI_API_KEY"
```

Use `GET /v1/batches` to list work, repeat `GET /v1/batches/{batch_id}`
until the status is terminal, or call `POST
/v1/batches/{batch_id}/cancel` while it is still running. Completed
output and error JSONL are exposed as `batch_output` Files; retrieve
their metadata with `GET /v1/files/{file_id}` and exact bytes with `GET
/v1/files/{file_id}/content`.

## Multipart Upload lifecycle

Create an Upload with the final byte count, filename, MIME type, and
`purpose=user_data`. Add one or more Parts, then pass their IDs to
`complete` in composition order. Completion returns the Upload with its
finished File.

```shell
UPLOAD_ID=$(curl -sS https://runapi.ai/v1/uploads \
  -H "Authorization: Bearer $RUNAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"bytes":1048576,"filename":"archive.bin","mime_type":"application/octet-stream","purpose":"user_data"}' \
  | jq -r .id)

PART_ID=$(curl -sS "https://runapi.ai/v1/uploads/$UPLOAD_ID/parts" \
  -H "Authorization: Bearer $RUNAPI_API_KEY" \
  -F data=@archive.part-01 | jq -r .id)

curl -sS "https://runapi.ai/v1/uploads/$UPLOAD_ID/complete" \
  -H "Authorization: Bearer $RUNAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"part_ids\":[\"$PART_ID\"]}"
```

Cancel an unfinished Upload with `POST /v1/uploads/{upload_id}/cancel`.
Repeating the same completion intent or cancellation is safe; a
competing completion, cancellation, or expiry returns `409
upload_state_conflict` when another terminal outcome already won.

## CLI and SDK resources

The CLI exposes the complete lifecycle:

```shell
runapi files create-file knowledge.pdf
runapi files list --order desc
runapi files retrieve file_123
runapi files content file_123 --output knowledge-copy.pdf
runapi files delete file_123

runapi uploads create --bytes 1048576 --filename archive.bin --mime-type application/octet-stream
runapi uploads add-part upload_123 archive.part-01
runapi uploads complete upload_123 --part-id part_123
runapi uploads cancel upload_123
```

Every Provider Client exposes `files` and `uploads`. JavaScript and PHP
use `createFile` / `deleteFile` / `addPart`; Python and Ruby use
`create_file` / `delete_file` / `add_part`; Go uses `CreateFile` /
`DeleteFile` / `AddPart`; Java uses `createFile` / `deleteFile` /
`addPart`. List, retrieve, content, create, complete, and cancel follow
each language's normal naming convention. See
[SDKs](https://runapi.ai/docs/resources/sdks.md) for package installation.

## Lifecycle behavior

File content is immutable. Deleting a File removes it from active
listings immediately and schedules storage cleanup. Upload completion
composes Parts only in the supplied order, and a successful completion
creates one File. Keep the original Part ID order when retrying an
uncertain completion response.

---

## More from RunAPI

- [Home](https://runapi.ai/.md)
- [Models](https://runapi.ai/models.md)
- [AI Video API Comparison](https://runapi.ai/ai-video-api-comparison.md)
- [OpenClaw Integration](https://runapi.ai/openclaw.md)
- [Best API for OpenClaw](https://runapi.ai/best-api-for-openclaw.md)
- [OpenClaw Image Generation](https://runapi.ai/openclaw-image-generation.md)
- [Hermes Agent Integration](https://runapi.ai/hermes-agent.md)
- [Pricing](https://runapi.ai/pricing.md)
- [Claude API Pricing](https://runapi.ai/claude-api-pricing.md)
- [ChatGPT API Pricing](https://runapi.ai/chatgpt-api-pricing.md)
- [OpenAI API Pricing](https://runapi.ai/openai-api-pricing.md)
- [Gemini API Pricing](https://runapi.ai/gemini-api-pricing.md)
- [Claude Code Pricing](https://runapi.ai/claude-code-pricing.md)
- [Claude Code API](https://runapi.ai/claude-code-api.md)
- [Claude Code vs Cursor](https://runapi.ai/claude-code-vs-cursor.md)
- [Claude Max vs API](https://runapi.ai/claude-max-vs-api.md)
- [Requesty Alternative](https://runapi.ai/requesty-alternative.md)
- [Docs](https://runapi.ai/docs/guides)
- [SDK](https://runapi.ai/sdk.md)
- [Skills](https://runapi.ai/skills.md)
- [MCP Server](https://runapi.ai/mcp.md)
- [CLI](https://runapi.ai/cli.md)
- [Feedback](https://runapi.ai/)
- [Contact](mailto:support@runapi.ai)
- [Terms](https://runapi.ai/terms.md)
- [Privacy](https://runapi.ai/privacy.md)
- [Site index for agents](https://runapi.ai/llms.txt)

Contact: contact@runapi.ai

## Structured data

```json
[
  {
    "@context": "https://schema.org",
    "inLanguage": "en",
    "@type": "WebSite",
    "name": "RunAPI",
    "url": "https://runapi.ai/",
    "potentialAction": {
      "@type": "SearchAction",
      "target": {
        "@type": "EntryPoint",
        "urlTemplate": "https://runapi.ai/models?q={search_term_string}"
      },
      "query-input": "required name=search_term_string"
    }
  },
  {
    "@context": "https://schema.org",
    "inLanguage": "en",
    "@type": "Organization",
    "name": "RunAPI",
    "url": "https://runapi.ai/",
    "logo": {
      "@type": "ImageObject",
      "url": "https://runapi.ai/icon.svg"
    },
    "sameAs": [
      "https://github.com/runapi-ai"
    ]
  },
  {
    "@context": "https://schema.org",
    "inLanguage": "en",
    "@type": "TechArticle",
    "headline": "Files and Uploads",
    "description": "Create, list, download, and delete persistent Files, or assemble a File from multipart Upload Parts.",
    "url": "https://runapi.ai/docs/resources/files",
    "mainEntityOfPage": "https://runapi.ai/docs/resources/files"
  }
]
```
