> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://notion.ferndocs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://notion.ferndocs.com/_mcp/server.

# Send a file upload

POST https://api.notion.com/v1/file_uploads/{file_upload_id}/send
Content-Type: multipart/form-data

Use this API to transmit file contents to Notion for a [file upload](/reference/file-upload).

For this endpoint, use a `Content-Type` of `multipart/form-data`, and provide your file contents under the `file` key.

The use of multipart form data is unique to this endpoint. Other Notion APIs, including [Create a file upload](/reference/create-a-file-upload) and [Complete a file upload](/reference/complete-a-file-upload), use JSON parameters.

Include a `boundary` with the `Content-Type` header of your request as per [RFC 2388](https://datatracker.ietf.org/doc/html/rfc2388). Most request libraries (e.g. `fetch`, `ky`) automatically handle this as long as you provide a form data object but don't overwrite the `Content-Type` explicitly.

For more tips and examples, view the [file upload guide](/reference/uploading-small-files#step-2-upload-file-contents).

When `mode=multi_part`, each part must include a form field `part_number` to indicate which part is being sent. Parts may be sent concurrently up to standard Notion API [rate limits](/reference/request-limits), and may be sent out of order as long as all parts (1, ..., `part_number`) are successfully sent before calling the [complete file upload API](https://developers.notion.com/reference/complete-a-file-upload).

The maximum allowed length of a file name is 900 bytes, including any file extension included in the file name or inferred based on the `content_type`. However, we recommend using shorter names for performance and easier file management and lookup using the [List file uploads](/reference/list-file-uploads) API.

Reference: https://notion.ferndocs.com/reference/send-a-file-upload

## Request

### Path parameters

- `file_upload_id` (string, required)

### Body (multipart/form-data)

This endpoint expects a multipart form containing a file.

- `file` (file, required) — The raw binary file contents to upload.
- `part_number` (string, optional) — When using a `mode=multi_part` File Upload to send files greater than 20 MB in parts, this is the current part number. Must be an integer between 1 and 1000 provided as a string form field.

## Response

### 200

200

## Errors

### 400 Bad Request Error

400

- `Send-a-file-uploadRequestBadRequestError`

### 403 Forbidden Error

403

- `object` (string, optional)
- `status` (integer, optional, default: 0)
- `code` (string, optional)
- `message` (string, optional)

## Types

### Validation Error (Invalid Status)

- `object` (string, optional)
- `status` (integer, optional, default: 0)
- `code` (string, optional)
- `message` (string, optional)

### Validation Error (Content Length Limit)

- `object` (string, optional)
- `status` (integer, optional, default: 0)
- `code` (string, optional)
- `message` (string, optional)

## Examples

**Request**

```json
{
  "file": "<file: string>"
}
```

**Response**

```json
{}
```

**SDK Code**

```python
import requests

# Path to the 1st part of the split-up file:
file_name = "image_to_upload_split_aa.png"

with open(file_name, "rb") as f:
    files = {
		    # Provide the MIME content type of the file
		    # as the 3rd argument.
        "file": (file_name, f, "image/png"),

        # Use a file name of `None` to treat this as a regular
        # form field and not a file.
        "part_number": (None, "1")
    }

    # URL can also be obtained from the `upload_url` field
    # of the Create File Upload response to avoid needing
    # to manually concatenate the `file_upload_id` and `/send`.
    url = f"https://api.notion.com/v1/file_uploads/{file_upload_id}/send"
    headers = {
      "Authorization": f"Bearer {NOTION_KEY}",
      "Notion-Version": "2022-06-28"
    }

    response = requests.post(url, headers=headers, files=files)
    print(response.text)
```

```ruby Ruby
require 'rest-client'
require 'mime/types'

file_name = "image_to_upload_split_aa.png"
file_upload_id = "YOUR_FILE_UPLOAD_ID" # Replace with actual upload ID
notion_key = "YOUR_NOTION_KEY" # Replace with your actual notion key

url = "https://api.notion.com/v1/file_uploads/#{file_upload_id}/send"

# Prepare the file
file = File.new(file_name, 'rb')
file_content_type = MIME::Types.type_for(file_name).first.content_type

# Prepare headers
headers = {
  Authorization: "Bearer #{notion_key}",
  'Notion-Version': '2022-06-28'
}

# Prepare multipart data
payload = {
  file: RestClient::Payload::File.new(file, filename: file_name, content_type: file_content_type),
  part_number: '1'
}

# Send the request
response = RestClient.post(url, payload, headers)

# Print the response
puts response.body
```

```javascript Node
const fs = require('fs');
const fetch = require('node-fetch');
const FormData = require('form-data');

const file_name = "image_to_upload_split_aa.png";
const file_upload_id = "YOUR_FILE_UPLOAD_ID"; // Replace with actual upload ID
const NOTION_KEY = "YOUR_NOTION_KEY"; // Replace with your actual notion key

// Prepare form data
const form = new FormData();
form.append('file', fs.createReadStream(file_name), {
    filename: file_name,
    contentType: 'image/png',
});
form.append('part_number', '1');

// Prepare headers
const headers = {
    'Authorization': `Bearer ${NOTION_KEY}`,
    'Notion-Version': '2022-06-28',
    ...form.getHeaders()
};

// Send the POST request
const url = `https://api.notion.com/v1/file_uploads/${file_upload_id}/send`;
fetch(url, {
    method: 'POST',
    headers: headers,
    body: form
})
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error('Error:', error));
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.notion.com/v1/file_uploads/file_upload_id/send"

	payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"string\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"part_number\"\r\n\r\n\r\n-----011000010111000001101001--\r\n")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Content-Type", "multipart/form-data; boundary=---011000010111000001101001")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.notion.com/v1/file_uploads/file_upload_id/send")
  .header("Content-Type", "multipart/form-data; boundary=---011000010111000001101001")
  .body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"string\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"part_number\"\r\n\r\n\r\n-----011000010111000001101001--\r\n")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.notion.com/v1/file_uploads/file_upload_id/send', [
  'multipart' => [
    [
        'name' => 'file',
        'filename' => 'string',
        'contents' => null
    ]
  ]
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.notion.com/v1/file_uploads/file_upload_id/send");
var request = new RestRequest(Method.POST);
request.AddParameter("multipart/form-data; boundary=---011000010111000001101001", "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"string\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"part_number\"\r\n\r\n\r\n-----011000010111000001101001--\r\n", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Content-Type": "multipart/form-data; boundary=---011000010111000001101001"]
let parameters = [
  [
    "name": "file",
    "fileName": "string"
  ],
  [
    "name": "part_number",
    "value": 
  ]
]

let boundary = "---011000010111000001101001"

var body = ""
var error: NSError? = nil
for param in parameters {
  let paramName = param["name"]!
  body += "--\(boundary)\r\n"
  body += "Content-Disposition:form-data; name=\"\(paramName)\""
  if let filename = param["fileName"] {
    let contentType = param["content-type"]!
    let fileContent = String(contentsOfFile: filename, encoding: String.Encoding.utf8)
    if (error != nil) {
      print(error as Any)
    }
    body += "; filename=\"\(filename)\"\r\n"
    body += "Content-Type: \(contentType)\r\n\r\n"
    body += fileContent
  } else if let paramValue = param["value"] {
    body += "\r\n\r\n\(paramValue)"
  }
}

let request = NSMutableURLRequest(url: NSURL(string: "https://api.notion.com/v1/file_uploads/file_upload_id/send")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```