You can’t upload files larger than 10MB to an API Gateway endpoint.

This limit is a hard constraint on the request payload size. It’s not configurable and applies to all API Gateway types (REST APIs, HTTP APIs, WebSocket APIs). The primary reason for this limit is to prevent denial-of-service attacks and manage resource consumption efficiently within the AWS infrastructure. Exceeding it results in a 413 Request Entity Too Large error.

Here’s how to work around it:

1. S3 Pre-signed URLs

Diagnosis: This is the most common and recommended approach for handling large file uploads.

How it works: Instead of sending the file directly through API Gateway, you first call an API Gateway endpoint to request a pre-signed URL for uploading to an S3 bucket. API Gateway then tells S3 to generate a temporary URL that has specific permissions to upload a file to a designated S3 object. Your client then uses this URL to upload the file directly to S3, bypassing API Gateway’s payload limit entirely. After the upload to S3 is complete, your client can optionally send a smaller notification (e.g., S3 object key, file metadata) back to API Gateway to trigger downstream processing.

Diagnosis Command/Check: There’s no specific command to "diagnose" this as a problem unless you’re already hitting the 413 error. The diagnosis is understanding the architecture.

Fix:

  1. API Gateway Lambda Authorizer/Integration: Create a Lambda function that uses the AWS SDK to generate an S3 pre-signed PUT URL. This Lambda function will be triggered by an API Gateway endpoint (e.g., POST /generate-upload-url).
    import boto3
    import json
    import os
    
    s3_client = boto3.client('s3')
    BUCKET_NAME = os.environ['BUCKET_NAME'] # e.g., 'my-large-file-uploads'
    EXPIRATION_SECONDS = 3600 # URL expiration time (1 hour)
    
    def lambda_handler(event, context):
        try:
            # Extract filename or generate a unique one
            # For simplicity, let's assume a filename is passed in the request body
            body = json.loads(event['body'])
            file_name = body.get('fileName', 'default_file_name')
            content_type = body.get('contentType', 'application/octet-stream')
    
            # Generate a unique key for S3 to avoid overwrites
            # You might use UUIDs or combine with user IDs for better uniqueness
            object_key = f"uploads/{file_name}"
    
            presigned_url = s3_client.generate_presigned_url(
                'put_object',
                Params={
                    'Bucket': BUCKET_NAME,
                    'Key': object_key,
                    'ContentType': content_type
                },
                ExpiresIn=EXPIRATION_SECONDS,
                HttpMethod='PUT'
            )
    
            return {
                'statusCode': 200,
                'headers': {
                    'Content-Type': 'application/json',
                    'Access-Control-Allow-Origin': '*' # For CORS, adjust as needed
                },
                'body': json.dumps({
                    'uploadUrl': presigned_url,
                    'objectKey': object_key,
                    'bucket': BUCKET_NAME
                })
            }
        except Exception as e:
            print(f"Error generating presigned URL: {e}")
            return {
                'statusCode': 500,
                'headers': {
                    'Content-Type': 'application/json',
                    'Access-Control-Allow-Origin': '*'
                },
                'body': json.dumps({'error': 'Failed to generate upload URL'})
            }
    
  2. Client-side Implementation: The client application (web browser, mobile app) makes a request to your API Gateway endpoint (POST /generate-upload-url) to get the uploadUrl and objectKey. Then, it uses a standard HTTP PUT request to upload the file directly to the provided uploadUrl.
    // Example using fetch API in a browser
    async function uploadFile(file) {
        const apiGatewayEndpoint = 'YOUR_API_GATEWAY_URL/generate-upload-url'; // Replace with your actual endpoint
    
        try {
            // 1. Get the pre-signed URL from API Gateway
            const response = await fetch(apiGatewayEndpoint, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    fileName: file.name,
                    contentType: file.type
                })
            });
    
            if (!response.ok) {
                throw new Error(`Failed to get upload URL: ${response.statusText}`);
            }
    
            const { uploadUrl, objectKey } = await response.json();
    
            // 2. Upload the file directly to S3 using the pre-signed URL
            const uploadResponse = await fetch(uploadUrl, {
                method: 'PUT',
                headers: {
                    'Content-Type': file.type // Crucial for S3 to identify the file type
                },
                body: file
            });
    
            if (!uploadResponse.ok) {
                throw new Error(`Failed to upload file to S3: ${uploadResponse.statusText}`);
            }
    
            console.log('File uploaded successfully to S3!', { objectKey: objectKey });
    
            // 3. (Optional) Notify API Gateway about the completed upload
            //    This could be a POST request to another endpoint with the objectKey
            //    to trigger downstream processing.
            // await fetch('YOUR_API_GATEWAY_URL/notify-upload-complete', { ... });
    
        } catch (error) {
            console.error('Upload process failed:', error);
        }
    }
    
    // Example usage:
    // const fileInput = document.getElementById('fileInput');
    // fileInput.addEventListener('change', (event) => {
    //     const file = event.target.files[0];
    //     if (file) {
    //         uploadFile(file);
    //     }
    // });
    
  3. S3 Bucket Configuration: Ensure your S3 bucket has appropriate permissions set up for the Lambda function (via IAM role) to PutObject.

Why it works: The pre-signed URL is a temporary, secure credential generated by S3 itself, allowing direct object uploads without passing through API Gateway’s request body limits. API Gateway is only involved in the initial request to obtain the URL, which is a very small payload.

2. Chunked Uploads with Direct S3 Upload (Client-Managed)

Diagnosis: Similar to the pre-signed URL approach, but the client handles breaking the file into chunks and reassembling them. This is more complex on the client side.

How it works: The client breaks the large file into smaller pieces (chunks). For each chunk, it requests a pre-signed URL from API Gateway (or directly from S3 if your architecture allows). The client then uploads each chunk to S3 using its respective pre-signed URL. Once all chunks are uploaded, the client sends a final request to API Gateway (again, small payload) to signal S3 to reassemble the chunks into the original file. This is often implemented using S3’s Multipart Upload API.

Diagnosis Command/Check: Again, this is an architectural choice. If you’re hitting the 413 error and considering chunking, this is the pattern.

Fix:

  1. Initiate Multipart Upload: The client requests an upload ID from S3 (or via a Lambda function that calls S3 create_multipart_upload).
  2. Upload Chunks: For each chunk of the file, the client requests a pre-signed URL for a UploadPart operation from S3 (or your API Gateway endpoint). The client then uploads the chunk.
  3. Complete Multipart Upload: Once all chunks are uploaded, the client sends a CompleteMultipartUpload request to S3 (or via API Gateway), providing the upload ID and a list of ETags for each uploaded part.

Why it works: Each individual chunk upload is a small payload, well within API Gateway’s limits. S3 handles the reassembly of these parts into the final object. This is more complex than single pre-signed URLs but offers more control and robustness for very large files or unreliable networks.

3. AWS Transfer Family (SFTP/FTPS/FTP)

Diagnosis: If your clients are already using SFTP, FTPS, or FTP, this might be a natural fit and completely bypasses API Gateway for file transfers.

How it works: You set up an AWS Transfer Family endpoint (e.g., an SFTP server). Your clients can then connect to this endpoint using standard SFTP clients and upload files directly to an S3 bucket or EFS file system. API Gateway is not involved in the file transfer itself.

Diagnosis Command/Check: This isn’t a workaround for an API Gateway error but an alternative transfer mechanism.

Fix:

  1. Create a Transfer Family Server: In the AWS console, go to AWS Transfer Family and create a server, choosing your protocol (SFTP is recommended for security).
  2. Configure Endpoint and Storage: Select an endpoint type (e.g., public VPC hosted) and choose your backend storage (S3 or EFS).
  3. Create Users: Add users to the Transfer Family server, mapping them to IAM roles that grant appropriate access to your S3 bucket or EFS.
  4. Client Connection: Provide clients with the server endpoint address, username, and their private key (for SFTP) to connect and upload files.

Why it works: This leverages dedicated file transfer protocols and AWS services designed for bulk file ingestion, completely sidestepping the HTTP request/response model that API Gateway operates on.

4. AWS DataSync or AWS Snowball Family

Diagnosis: For extremely large datasets or recurring large file transfers, API Gateway is simply the wrong tool.

How it works:

  • AWS DataSync: If you have large amounts of data already on-premises or in another cloud and want to move it to S3. DataSync is a managed service that automates and accelerates data transfers.
  • AWS Snowball Family: For petabyte-scale data transfers where network bandwidth is a bottleneck. You order a physical device from AWS, load your data onto it, and ship it back to AWS for ingestion into S3.

Diagnosis Command/Check: These are infrastructure-level solutions, not API Gateway workarounds.

Fix:

  • DataSync: Set up DataSync agents, configure source and destination locations (S3 bucket), and run a task.
  • Snowball: Order a Snowball device, install the client, transfer data, and ship the device back.

Why it works: These services are designed for bulk data migration and are not limited by HTTP request sizes or network latency in the same way API Gateway is.

The Next Error You’ll Hit

If you implement S3 pre-signed URLs and your Lambda function for generating them is misconfigured, you’ll likely hit a 502 Bad Gateway error from API Gateway if the Lambda execution fails, or a 403 Forbidden error from S3 if the IAM role lacks permissions.

Want structured learning?

Take the full Apigateway course →