Skip to main content

Overview

After receiving a pre-signed upload URL from the /upload-url endpoint, you upload your file directly to storage. This direct-to-storage approach ensures fast uploads without bottlenecking through the API server.

Important: Use PUT Method with Raw Bytes

Critical Implementation Details:
  • Use PUT method (not POST)
  • Send raw file bytes as the request body (not multipart/form-data)
  • Set the appropriate Content-Type header
  • Do not include authentication headers (the URL is pre-signed)

Request

import requests

# Assuming you have upload_url from the previous step
with open('report.pdf', 'rb') as f:
    file_bytes = f.read()

response = requests.put(
    upload_url,
    data=file_bytes,  # ← Use 'data', NOT 'files'
    headers={"Content-Type": "application/pdf"}
)

# Check for success
if response.status_code in [200, 201]:
    print("Upload successful!")
else:
    print(f"Upload failed: {response.status_code}")
    print(response.text)

Request Details

  • Method: PUT (not POST)
  • URL: The upload_url from the /upload-url response
  • Body: Raw file bytes
  • Headers:
    • Content-Type: The MIME type of your file (e.g., application/pdf, text/csv, image/png)

Content-Type Headers by File Type

File TypeContent-Type
PDFapplication/pdf
CSVtext/csv
Excelapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet
JSONapplication/json
Plain Texttext/plain
PNGimage/png
JPEGimage/jpeg
GIFimage/gif
WebPimage/webp

Response

Success

Status Code: 200 OK or 201 Created The response body is typically empty or contains storage metadata (not needed for your application).

Common Errors

Upload URL Expired

Status Code: 403 Forbidden
{
  "error": "Token has expired"
}
Solution: Request a new upload URL. Pre-signed URLs expire after ~60 seconds.

Invalid Method

Status Code: 405 Method Not Allowed Solution: Use PUT method, not POST.

File Too Large

Status Code: 413 Payload Too Large Solution: Ensure file is under 50 MB limit.

Common Implementation Mistakes

❌ Wrong: Using multipart/form-data

# DON'T DO THIS
with open('file.pdf', 'rb') as f:
    response = requests.put(
        upload_url,
        files={'file': f}  # ❌ Wrong
    )

✅ Correct: Using raw bytes

# DO THIS
with open('file.pdf', 'rb') as f:
    response = requests.put(
        upload_url,
        data=f.read()  # ✅ Correct
    )

❌ Wrong: Using POST method

# DON'T DO THIS
response = requests.post(upload_url, data=file_bytes)  # ❌ Wrong method

✅ Correct: Using PUT method

# DO THIS
response = requests.put(upload_url, data=file_bytes)  # ✅ Correct method

❌ Wrong: Including authentication

# DON'T DO THIS
response = requests.put(
    upload_url,
    data=file_bytes,
    headers={
        "Authorization": "Bearer ...",  # ❌ Not needed
        "Content-Type": "application/pdf"
    }
)

✅ Correct: No authentication (URL is pre-signed)

# DO THIS
response = requests.put(
    upload_url,
    data=file_bytes,
    headers={"Content-Type": "application/pdf"}  # ✅ Just Content-Type
)

Complete Example with Error Handling

import requests

def upload_file(upload_url, file_path, content_type):
    """
    Upload file to pre-signed URL with proper error handling.
    """
    try:
        with open(file_path, 'rb') as f:
            file_bytes = f.read()
        
        response = requests.put(
            upload_url,
            data=file_bytes,
            headers={"Content-Type": content_type},
            timeout=60  # 60 second timeout
        )
        
        if response.status_code in [200, 201]:
            return {"success": True, "size": len(file_bytes)}
        else:
            return {
                "success": False,
                "error": f"Upload failed with status {response.status_code}",
                "details": response.text
            }
    
    except FileNotFoundError:
        return {"success": False, "error": "File not found"}
    
    except requests.exceptions.Timeout:
        return {"success": False, "error": "Upload timed out"}
    
    except Exception as e:
        return {"success": False, "error": str(e)}

# Usage
result = upload_file(
    upload_url="https://storage.supabase.co/...",
    file_path="report.pdf",
    content_type="application/pdf"
)

if result["success"]:
    print(f"Uploaded {result['size']} bytes successfully")
else:
    print(f"Error: {result['error']}")

Next Steps

After successfully uploading your file:
  1. Confirm the upload to trigger automatic file processing → See Confirm Upload
  2. Use the file_id in chat completions → See Chat Completion API
  3. Retrieve file metadata to see processing results → See Get Files Metadata

Performance Tips

  1. File Size: Validate file size before upload to avoid timeouts
  2. Timeout: Set appropriate timeouts (60+ seconds for large files)
  3. Retry Logic: Implement retry with exponential backoff for network errors
  4. Parallel Uploads: For multiple files, upload them in parallel
  5. Progress Tracking: Use streaming libraries to track upload progress for large files