Beecargo

Get set up

  • ○Sign in
  • ○Connect your agent
  • ○Share a file

API

OverviewUpload a fileRemote uploadShare settingsClaim a fileAgent APIWebhooksRetrieve a fileGet file infoList files & foldersDelete a file

MCP

OverviewRegister an agentUpload a fileUpload statusRemote uploadLarge uploads & jobsFoldersShare settingsSeller payoutsBuy a priced shareClaim fileSearch toolsCreate checkoutRetrieve a fileGet file infoList filesDelete a fileFiles from a runUpload delegation

PreviousOverviewNextRemote upload
BlogPricingDashboardPrivacyTerms

Upload a file

Send a file to Beecargo with the HTTP API.


Same flow in MCP: beecargo_upload

Endpoint

POSThttps://api.beecargo.net/files/upload

Use this for files under 4MB. The samples below also show how larger files use multipart upload.

Authentication

With an API key: send Authorization: Bearer YOUR_API_KEY.

Authorization: Bearer YOUR_API_KEY

Without a key: 1 GB per file; expires after 3 days.

How uploads choose a path

Files under 4MB use POST /files/upload. Files 4MB and larger use multipart (init → batch-urls with per-part SHA-256 digests → part PUTs → complete). The parallel sample below calls uploadFile() and picks the path for you.

You can upload parts one after another for reliability on proxies, VPNs, and flaky networks. For speed, upload several parts at once (about 3–6), as the Beecargo website, CLI, and stdio MCP local path do.

Request examples

Copy a sample into your project. Direct upload tabs cover small files; use the parallel sample for large files.

Faster uploads

For large files, upload several multipart chunks at once (about 3–6 concurrent PUTs). The Beecargo website, CLI, and stdio MCP local path already do this; the sample below shows the same REST pattern.

See "Faster parallel upload samples" further down this page.

With API key:

# For files < 4MB - Use Bearer token format (OAuth 2.0 standard)

curl -L -X POST https://api.beecargo.net/files/upload \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@/path/to/document.pdf"

# For files >= 4MB, use the client library examples below

# (multipart upload requires multiple API calls)

Anonymous Upload (NOT saved to account):

# File will NOT appear in your dashboard and expires in 3 days

curl -L -X POST https://api.beecargo.net/files/upload \
  -F "file=@/path/to/document.pdf"

Parameters

ParameterTypeRequiredDescription
fileFileYesThe file to upload
folderIdStringNoOptional folder id
visibilityunlisted | publicNoPublic requires a claimed username
directBooleanNoPro: start download when the share opens
retentionttl | foreverNoPublic Pro shares may use forever
expiresAtString (ISO)NoExplicit expiration for TTL retention
ttlStringNoKeep-time preset from now, such as 1h, 24h, or 7d
onceBooleanNoWhen true, the share can be downloaded only once
maxDownloadsNumberNoMax downloads allowed (ignored when once is true)
protectBooleanNoCreate a one-time unlock code and private delivery link
handoffMessageStringNoOptional delivery-link note, maximum 480 characters
runIdStringNoOptional pipeline id to group files; list them later with GET /files/list?runId=
openShareBooleanNoAuthenticated: open a growable multi-file Shipment (response shortId is the share). Later uploads pass shareShortId.
shareShortIdStringNoAuthenticated: attach this file to an existing growable Shipment from a prior openShare upload

Response

The response includes a temporary signed URL (about 24 hours). For a lasting share link, use https://beecargo.net/d/{shortId}. Machine downloads still wait for the safety check.

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "document.pdf",
    "size": 1048576,
    "url": "https://signed-url.cloudflare.com/...",
    "mimeType": "application/pdf",
    "createdAt": "2025-11-16T12:00:00.000Z",
    "isAnonymous": false,
    "expiresAt": null,
    "shortId": "abc123"
  }
}

Anonymous uploads set isAnonymous to true and expire after about 3 days. For safe retries on write requests, send an Idempotency-Key header.

Faster parallel upload samples

3–6× faster uploads

Sequential part PUTs finish one chunk at a time. Parallel clients (website, CLI, stdio MCP, and the sample below) upload several chunks at once for large files.

One chunk at a time

200 parts × 2s ≈ 400s (~6.7 min)

Four chunks at once

200 parts ÷ 4 × 2s ≈ 100s (~1.7 min)

Python (parallel)

import hashlib
import os
import time
from concurrent.futures import ThreadPoolExecutor, as_completed

import requests

API_KEY = 'YOUR_API_KEY'  # Get from dashboard settings
BASE_URL = 'https://api.beecargo.net'
MULTIPART_THRESHOLD = 4 * 1024 * 1024  # 4MB

def get_optimal_parallelism(file_size):
    """Determine optimal number of parallel uploads based on file size"""
    if file_size > 50 * 1024**3:  # > 50GB
        return 3
    elif file_size > 10 * 1024**3:  # > 10GB
        return 4
    elif file_size > 1 * 1024**3:  # > 1GB
        return 5
    else:
        return 6

def upload_small_file(file_path, folder_id=None):
    """Upload small files (< 4MB) directly"""
    url = f'{BASE_URL}/files/upload'
    file_size = os.path.getsize(file_path)
    file_name = os.path.basename(file_path)

    print(f"Uploading {file_name} ({file_size / (1024**2):.2f} MB)...")

    headers = {}
    if API_KEY:
        headers['Authorization'] = f'Bearer {API_KEY}'

    with open(file_path, 'rb') as f:
        files = {'file': (file_name, f)}
        data = {}
        if folder_id:
            data['folderId'] = folder_id

        response = requests.post(url, headers=headers, files=files, data=data)

    response.raise_for_status()
    result = response.json()
    if not result.get('success'):
        raise Exception(result.get('error', 'Upload failed'))

    print("Upload completed!")
    return result['data']

def upload_large_file_parallel(file_path, folder_id=None):
    """Upload large files (>= 4MB) with parallel multipart parts"""
    file_size = os.path.getsize(file_path)
    file_name = os.path.basename(file_path)

    headers = {'Content-Type': 'application/json'}
    if API_KEY:
        headers['Authorization'] = f'Bearer {API_KEY}'

    print(f"Initializing upload for {file_name} ({file_size / (1024**3):.2f} GB)...")
    init_res = requests.post(
        f'{BASE_URL}/files/multipart/init',
        headers=headers,
        json={
            'fileName': file_name,
            'fileSize': file_size,
            'fileType': 'application/octet-stream',
            'folderId': folder_id
        }
    )
    init_res.raise_for_status()
    init_data = init_res.json()
    upload_id = init_data['uploadId']
    key = init_data['key']
    chunk_size = init_data['chunkSize']
    total_parts = init_data['totalParts']
    upload_session_token = init_data.get('uploadSessionToken')

    print(f"Upload initialized: {total_parts} parts x {chunk_size / (1024**2):.1f} MB")

    part_digests = {}
    with open(file_path, 'rb') as f:
        for part_number in range(1, total_parts + 1):
            chunk = f.read(chunk_size)
            part_digests[str(part_number)] = hashlib.sha256(chunk).hexdigest()

    batch_body = {
        'key': key,
        'uploadId': upload_id,
        'totalParts': total_parts,
        'partDigests': part_digests,
    }
    if upload_session_token:
        batch_body['uploadSessionToken'] = upload_session_token

    print(f"Getting presigned URLs for {total_parts} parts...")
    urls_res = requests.post(
        f'{BASE_URL}/files/multipart/batch-urls',
        headers=headers,
        json=batch_body,
    )
    urls_res.raise_for_status()
    urls_data = urls_res.json()
    if not urls_data.get('success'):
        raise Exception(urls_data.get('error', 'Failed to get upload URLs'))

    urls_dict = urls_data['urls']
    print("Got all presigned URLs")

    parallelism = get_optimal_parallelism(file_size)
    print(f"Uploading {total_parts} parts with {parallelism}x parallelism...")
    start_time = time.time()
    uploaded_parts = []
    completed_parts = 0

    def upload_part(part_number):
        url = urls_dict[str(part_number)]

        with open(file_path, 'rb') as f:
            f.seek((part_number - 1) * chunk_size)
            chunk = f.read(chunk_size)

        max_retries = 3
        for attempt in range(max_retries):
            try:
                res = requests.put(url, data=chunk, timeout=300)
                res.raise_for_status()
                etag = res.headers.get('ETag', '').strip('"')
                return {'partNumber': part_number, 'etag': etag}
            except Exception:
                if attempt == max_retries - 1:
                    raise
                time.sleep(2 ** attempt)

    with ThreadPoolExecutor(max_workers=parallelism) as executor:
        futures = {
            executor.submit(upload_part, i): i for i in range(1, total_parts + 1)
        }

        for future in as_completed(futures):
            part_number = futures[future]
            try:
                result = future.result()
                uploaded_parts.append(result)
                completed_parts += 1

                elapsed = time.time() - start_time
                uploaded_bytes = completed_parts * chunk_size
                speed = uploaded_bytes / elapsed / (1024**2) if elapsed > 0 else 0
                progress = (completed_parts / total_parts) * 100
                eta = (
                    (total_parts - completed_parts)
                    * chunk_size
                    / (uploaded_bytes / elapsed)
                    if uploaded_bytes > 0
                    else 0
                )

                bar_length = 30
                filled = int(bar_length * completed_parts / total_parts)
                bar = '#' * filled + '-' * (bar_length - filled)

                print(
                    f"\r[{bar}] {progress:.1f}% | {completed_parts}/{total_parts} parts | "
                    f"{speed:.1f} MB/s | ETA: {eta:.0f}s",
                    end='',
                    flush=True,
                )
            except Exception as e:
                print(f"\nFailed to upload part {part_number}: {e}")
                raise

    elapsed_total = time.time() - start_time
    avg_speed = file_size / elapsed_total / (1024**2)
    print(f"\nUpload completed in {elapsed_total:.1f}s (avg: {avg_speed:.1f} MB/s)")

    uploaded_parts.sort(key=lambda x: x['partNumber'])

    print("Finalizing upload...")
    complete_body = {
        'key': key,
        'uploadId': upload_id,
        'parts': uploaded_parts,
        'fileName': file_name,
        'fileSize': file_size,
        'contentType': 'application/octet-stream',
        'folderId': folder_id,
    }
    if upload_session_token:
        complete_body['uploadSessionToken'] = upload_session_token

    complete_res = requests.post(
        f'{BASE_URL}/files/multipart/complete',
        headers=headers,
        json=complete_body,
    )
    complete_res.raise_for_status()
    result = complete_res.json()
    if not result.get('success'):
        raise Exception(result.get('error'))

    print("File saved")
    return result['file']

def upload_file(file_path, folder_id=None):
    """Main upload function - direct under 4MB, parallel multipart otherwise"""
    file_size = os.path.getsize(file_path)

    if file_size < MULTIPART_THRESHOLD:
        return upload_small_file(file_path, folder_id)
    return upload_large_file_parallel(file_path, folder_id)

if __name__ == '__main__':
    try:
        result = upload_file('./large_file.zip')
        print(f"\nSuccess! Share: https://beecargo.net/d/{result['shortId']}")
    except Exception as e:
        print(f'Upload failed: {e}')

JavaScript (parallel)

See the Python parallel sample above.

What changes in the parallel samples

  • Several chunks upload at the same time (ThreadPoolExecutor in Python).
  • Worker count scales with file size (about 3–6).
  • Each part’s SHA-256 digest is sent in batch-urls before PUTs.
  • Presigned URLs are fetched up front so chunks do not wait on each other.
  • Each chunk can retry on its own.
  • Progress counts finished chunks across all workers.

Error response

{
  "success": false,
  "error": "Unauthorized"
}

Try it

Sign in to try this against your account.

Sign in