# moonpush.com

> Free, temporary file sharing API. Upload any file up to 1 GB, get an instant download link. Built for AI agents, LLMs, developers, and automation. No signup required.

## Quick Start

```bash
# Upload a file
curl -F "file=@yourfile.pdf" https://moonpush.com/api/upload

# Download a file
curl -OJL https://moonpush.com/api/dl/<id>
```

---

## API Endpoints

Base URL: `https://moonpush.com`

| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | /api/upload | Upload a file |
| GET | /api/dl/{id} | Download a file by UUID |
| GET | /api/share/{code} | Resolve a share link code |
| POST | /api/folder/new | Create a folder |
| GET | /api/folder/{id} | Get folder metadata and file list |
| POST | /api/folder/{id} | Add a file to a folder |
| GET | /api/folder-download/{id} | Download entire folder as .zip |

---

### POST /api/upload

Upload a file. Accepts `multipart/form-data` with a `file` field.

**curl:**

```bash
curl -F "file=@report.csv" https://moonpush.com/api/upload
```

**Python:**

```python
import requests

with open("report.csv", "rb") as f:
    r = requests.post("https://moonpush.com/api/upload", files={"file": f})

data = r.json()
print(data["downloadUrl"])
```

**JavaScript/Node.js:**

```javascript
const form = new FormData();
form.append("file", fs.createReadStream("report.csv"));

const res = await fetch("https://moonpush.com/api/upload", {
  method: "POST",
  body: form,
});
const data = await res.json();
console.log(data.downloadUrl);
```

**Response (201 Created):**

```json
{
  "id": "9f6b14e5-c642-461c-a19e-6c348287c189",
  "originalName": "report.csv",
  "mimeType": "text/csv",
  "size": 12048,
  "uploadedAt": "2026-03-15T05:35:33.322Z",
  "downloadUrl": "https://moonpush.com/api/dl/9f6b14e5-c642-461c-a19e-6c348287c189",
  "directUrl": "https://cdn.moonpush.com/files/9f6b14e5-.../report.csv",
  "expiresIn": "30 minutes",
  "cli": "curl -OJL https://moonpush.com/api/dl/9f6b14e5-c642-461c-a19e-6c348287c189"
}
```

**Errors:**

| Status | Reason |
|--------|--------|
| 400 | No file provided, empty file, blocked file type, or suspicious filename |
| 413 | File too large |
| 429 | Rate limit exceeded |

---

### GET /api/dl/{id}

Download a file by its UUID. If the file was uploaded with encryption and the server has the key, it is **automatically decrypted** before serving.

```bash
curl -OJL https://moonpush.com/api/dl/9f6b14e5-c642-461c-a19e-6c348287c189
```

`-O` saves to disk, `-J` uses the server-provided filename, `-L` follows redirects.

**Response headers:**

- `Content-Type` — original MIME type (HTML is served as `text/plain` for security)
- `Content-Disposition: attachment; filename="originalname.ext"` — forces download with original filename
- `Access-Control-Allow-Origin: *` — CORS enabled for all origins
- `X-Content-Type-Options: nosniff` — prevents MIME sniffing

**Status codes:**

| Status | Meaning |
|--------|---------|
| 200 | File downloaded successfully (decrypted if encrypted) |
| 202 | File is still uploading — retry shortly |
| 400 | Invalid file ID format (must be UUID v4) |
| 404 | File not found (may have expired) |
| 410 | File expired (30 min TTL exceeded) or upload failed |
| 429 | Rate limit exceeded |
| 502 | File data unavailable from storage |

---

### GET /api/share/{code}

Resolve a share link short code to a file or folder ID.

Share links look like `https://moonpush.com/share/ABC123` (sometimes with `#encryptionKey` appended for browser use — ignore the fragment).

```bash
curl -s https://moonpush.com/api/share/ABC123
```

**Response (file):**

```json
{
  "type": "pipe",
  "pipeId": "9f6b14e5-c642-461c-a19e-6c348287c189",
  "status": "complete",
  "originalName": "report.csv",
  "mimeType": "text/csv",
  "size": 12048
}
```

**Response (folder):**

```json
{
  "type": "folder",
  "folderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
```

**Status codes:**

| Status | Meaning |
|--------|---------|
| 200 | Resolved successfully |
| 404 | Code not found |
| 410 | Link expired |

---

### Downloading files shared via browser (share links)

When a user shares a file via the browser, the URL looks like:
`https://moonpush.com/share/CODE#ENCRYPTION_KEY`

**To download as an AI agent or from CLI:**

```bash
# Step 1: Extract the code (part before #)
CODE="ABC123"

# Step 2: Resolve to get the file ID
PIPE_ID=$(curl -s https://moonpush.com/api/share/$CODE | jq -r .pipeId)

# Step 3: Download the file (server decrypts automatically)
curl -OJL https://moonpush.com/api/dl/$PIPE_ID
```

**IMPORTANT for AI agents:** Do NOT try to fetch `https://moonpush.com/share/CODE` directly — that returns an HTML page, not the file. Always use the API: resolve via `/api/share/{code}`, then download via `/api/dl/{pipeId}`.

**Python helper:**

```python
import requests

def download_share(share_url: str, save_path: str = None) -> str:
    # Extract code from URL (before # fragment)
    code = share_url.split("/share/")[-1].split("#")[0]

    # Resolve share code
    r = requests.get(f"https://moonpush.com/api/share/{code}")
    data = r.json()

    if data.get("type") == "folder":
        # Download folder as zip
        folder_id = data["folderId"]
        r = requests.get(f"https://moonpush.com/api/folder/{folder_id}/download")
        filename = save_path or "folder.zip"
    else:
        # Download single file
        pipe_id = data["pipeId"]
        r = requests.get(f"https://moonpush.com/api/dl/{pipe_id}")
        filename = save_path or data.get("originalName", "download")

    with open(filename, "wb") as f:
        f.write(r.content)
    return filename
```

---

## Folders

Folders let you group multiple files under a single shareable link.

### POST /api/folder/new

Create a new folder.

```bash
curl -s -X POST https://moonpush.com/api/folder/new \
  -H "Content-Type: application/json" \
  -d '{"name": "Project Files", "mode": "shared"}'
```

**Request body (all optional):**

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| name | string | "Untitled Folder" | Folder display name |
| mode | string | "delivery" | Access mode: `delivery`, `shared`, or `submission` |
| expiryMinutes | number | 30 | Auto-delete after N minutes |

**Folder modes:**

| Mode | Who can upload | Who can download |
|------|---------------|-----------------|
| delivery | Owner only | Anyone |
| shared | Anyone | Anyone |
| submission | Visitors only | Owner only |

**Response (201 Created):**

```json
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "shortCode": "xK9mR2pL4nB",
  "folderUrl": "https://moonpush.com/folder/xK9mR2pL4nB",
  "ownerToken": "base64url-encoded-token",
  "name": "Project Files",
  "mode": "shared",
  "expiryMinutes": 30,
  "expiresAt": "2026-03-15T06:05:33.322Z"
}
```

**Save the `ownerToken`** — you need it to manage the folder and download submission files.

---

### GET /api/folder/{id}

Get folder metadata and file list.

```bash
curl -s https://moonpush.com/api/folder/FOLDER_ID \
  -H "x-owner-token: YOUR_TOKEN"
```

The `x-owner-token` header is optional but required to see files in submission-mode folders.

**Response:**

```json
{
  "id": "a1b2c3d4-...",
  "shortCode": "xK9mR2pL4nB",
  "name": "Project Files",
  "mode": "shared",
  "expiresAt": "2026-03-15T06:05:33.322Z",
  "isOwner": true,
  "fileCount": 3,
  "files": [
    {
      "id": "file-uuid",
      "pipeId": "pipe-uuid",
      "originalName": "report.csv",
      "mimeType": "text/csv",
      "size": 12048,
      "status": "complete",
      "addedAt": "2026-03-15T05:35:33.322Z",
      "downloadUrl": "https://cdn.moonpush.com/files/pipe-uuid/report.csv"
    }
  ]
}
```

To download individual files, use `GET /api/dl/{pipeId}` (not the `downloadUrl` CDN link, which lacks headers).

---

### GET /api/folder-download/{id}

Download all files in a folder as a single .zip archive.

```bash
# Download folder as zip
curl -OJL https://moonpush.com/api/folder/FOLDER_ID/download

# For submission folders, include owner token
curl -OJL https://moonpush.com/api/folder/FOLDER_ID/download \
  -H "x-owner-token: YOUR_TOKEN"
```

**Response:** Binary .zip file with `Content-Disposition: attachment; filename="FolderName.zip"`

**Status codes:**

| Status | Meaning |
|--------|---------|
| 200 | Zip downloaded successfully |
| 400 | No downloadable files in folder |
| 403 | Submission folder — owner token required |
| 404 | Folder not found |
| 410 | Folder expired |
| 429 | Rate limit exceeded |

If some files failed to download, the zip will contain the files that succeeded and the `X-Skipped-Files` header indicates how many were skipped.

---

### Downloading a folder from a share link

When a user shares a folder link like `https://moonpush.com/folder/CODE`:

```bash
# Step 1: Resolve the code
FOLDER_ID=$(curl -s https://moonpush.com/api/share/CODE | jq -r .folderId)

# Step 2: Download as zip
curl -OJL https://moonpush.com/api/folder/$FOLDER_ID/download
```

**Or download files individually:**

```bash
# Step 1: Resolve the code
FOLDER_ID=$(curl -s https://moonpush.com/api/share/CODE | jq -r .folderId)

# Step 2: List files
FILES=$(curl -s https://moonpush.com/api/folder/$FOLDER_ID | jq -r '.files[] | select(.status=="complete") | .pipeId')

# Step 3: Download each file
for PIPE_ID in $FILES; do
  curl -OJL https://moonpush.com/api/dl/$PIPE_ID
done
```

---

### GET /api/share/{code}

Resolve a share link short code to a pipe ID. Share links look like `https://moonpush.com/share/ABC123#encryptionKey`.

```bash
curl -s https://moonpush.com/api/share/ABC123
```

**Response:**

```json
{
  "type": "pipe",
  "pipeId": "9f6b14e5-c642-461c-a19e-6c348287c189"
}
```

Then download the file:

```bash
curl -OJL https://moonpush.com/api/dl/9f6b14e5-c642-461c-a19e-6c348287c189
```

---

### Downloading files shared via browser (share links)

When a user shares a file via the browser, the URL looks like:
`https://moonpush.com/share/CODE#ENCRYPTION_KEY`

To download as an AI agent or from CLI:

```bash
# Step 1: Resolve share code to pipe ID
PIPE_ID=$(curl -s https://moonpush.com/api/share/CODE | jq -r .pipeId)

# Step 2: Download the file (server decrypts automatically)
curl -OJL https://moonpush.com/api/dl/$PIPE_ID
```

The server stores the encryption key and decrypts automatically when downloading via `/api/dl/`. No manual decryption needed.

---

## Accepted File Types

Most file types are accepted. Executables (.exe, .dll, .bat, .ps1, .vbs, etc.) are blocked for security.

Common examples:

| Category | Extensions |
|----------|-----------|
| Documents | .pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .txt, .rtf, .odt |
| Images | .png, .jpg, .jpeg, .gif, .svg, .webp, .bmp, .ico, .tiff |
| Video | .mp4, .mov, .avi, .mkv, .webm |
| Audio | .mp3, .wav, .ogg, .flac, .m4a |
| Code | .py, .js, .ts, .go, .rs, .java, .c, .cpp, .rb, .php, .sh |
| Data | .csv, .json, .xml, .yaml, .toml, .sql, .parquet |
| Archives | .zip, .tar, .gz, .7z, .rar, .tar.gz |
| Other | .dmg, .iso, .wasm, .bin |

---

## Limits

| Limit | Value |
|-------|-------|
| Max file size | 1 GB (100 GB for browser uploads) |
| File types | Most types accepted. Executables blocked |
| Expiry | 30 minutes after upload (auto-deleted) |
| Authentication | None required |
| Rate limiting | 15 uploads/hour, 50/day per IP |
| Max filename length | 255 characters |
| Max files per folder | 500 |

---

## Security

| Protection | Details |
|-----------|---------|
| **No execution** | Files are stored as raw blobs. Never executed or processed. |
| **UUID-only access** | Files accessible only via UUID v4 (122 bits of randomness). |
| **Forced download** | `Content-Disposition: attachment` — never rendered in browser. |
| **No MIME sniffing** | `X-Content-Type-Options: nosniff` header on all downloads. |
| **HTML neutralized** | HTML files served as `text/plain` to prevent XSS. |
| **Auto-expiry** | Files deleted after 30 minutes. No persistent storage. |
| **CORS enabled** | `Access-Control-Allow-Origin: *` on all API responses. |

---

## Notes for AI Agents

**Critical rules:**

1. The multipart form field name MUST be exactly `file`.
2. Files auto-delete after **30 minutes**. Do not rely on MoonPush for persistent storage.
3. There is **no endpoint to list or browse files**. Files are private and only accessible by UUID.
4. CORS is enabled (`Access-Control-Allow-Origin: *`) on all API endpoints.

**Handling share links:**

- When a user gives you a URL like `https://moonpush.com/share/CODE#KEY`, do NOT fetch the URL directly. It returns HTML.
- Instead: (1) extract the code before `#`, (2) call `GET /api/share/{code}` to get the ID, (3) download via `GET /api/dl/{pipeId}` or `GET /api/folder-download/{folderId}`.
- Check the `type` field in the share response: `"pipe"` = single file, `"folder"` = folder.

**Handling folders:**

- To download all files at once: `GET /api/folder-download/{id}` returns a .zip file.
- To download files individually: get file list from `GET /api/folder/{id}`, then download each via `GET /api/dl/{pipeId}`.
- For submission folders, include `x-owner-token` header.

**Error handling:**

- `404` = file/folder not found (may have expired)
- `410` = explicitly expired
- `202` = file still uploading, retry in a few seconds
- `429` = rate limited, wait and retry

**Complete example — download from share link:**

```python
import requests

def download_from_moonpush(url: str) -> str:
    code = url.split("/share/")[-1].split("/")[-1].split("#")[0]

    # Also handle /folder/ URLs
    if "/folder/" in url:
        code = url.split("/folder/")[-1].split("#")[0]

    info = requests.get(f"https://moonpush.com/api/share/{code}").json()

    if "error" in info:
        raise Exception(info["error"])

    if info["type"] == "folder":
        r = requests.get(f"https://moonpush.com/api/folder-download/{info['folderId']}")
        filename = "folder.zip"
    else:
        r = requests.get(f"https://moonpush.com/api/dl/{info['pipeId']}")
        cd = r.headers.get("content-disposition", "")
        filename = info.get("originalName", "download")

    r.raise_for_status()
    with open(filename, "wb") as f:
        f.write(r.content)
    return filename
```

---

## MCP Server

An official MCP server is available on GitHub: [moonpush-mcp](https://github.com/aamirmursleen/moonpush-mcp)

```bash
pip install "mcp[cli]"
claude mcp add moonpush python3 /path/to/mcp-server/server.py
```

**Tools:**

| Tool | Description |
|------|-------------|
| `upload_file(file_path)` | Upload a file from disk, returns download link |
| `download_file(file_id, save_path?)` | Download a file by ID or URL |
| `download_share(share_url, save_path?)` | Download from a share link — resolves and decrypts automatically |
| `upload_text(content, filename?)` | Upload text/code as a file, returns download link |

---

*Built by MoonPush.*
