> ## Documentation Index
> Fetch the complete documentation index at: https://developer.box.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Recursively upload a folder with Python

> Learn how to upload a local folder tree to Box with a recursive Python script that creates folders, uploads files, and handles naming conflicts.

export const SignupCTA = ({children}) => {
  return <div className="flex flex-wrap items-center gap-4 p-5 rounded-lg border border-gray-200 dark:border-gray-700 my-6" style={{
    background: "linear-gradient(135deg, rgba(0, 97, 213, 0.06), rgba(0, 97, 213, 0.02))"
  }}>
      <div className="flex-1 text-sm leading-relaxed text-gray-700 dark:text-gray-300" style={{
    minWidth: "280px"
  }}>
        {children}
      </div>
      <div className="flex flex-col items-center gap-2">
        <a href="https://account.box.com/signup/developer#ty9l3" className="signup-cta-button inline-flex items-center whitespace-nowrap px-5 py-2 text-sm font-semibold text-white no-underline">
          Get started for free
        </a>
        <a href="https://account.box.com/developers/console" className="signup-cta-login text-xs text-gray-500 dark:text-gray-400 no-underline whitespace-nowrap">
          Already have an account? Log in
        </a>
      </div>
    </div>;
};

export const RelatedLinks = ({title, items = []}) => {
  const getBadgeClass = badge => {
    if (!badge) return "badge-default";
    const badgeType = badge.toLowerCase().replace(/\s+/g, "-");
    return `badge-${badge === "ガイド" ? "guide" : badgeType}`;
  };
  if (!items || items.length === 0) {
    return null;
  }
  return <div className="my-8">
      {}
      <h3 className="text-sm font-bold uppercase tracking-wider mb-4">{title}</h3>

      {}
      <div className="flex flex-col gap-3">
        {items.map((item, index) => <a key={index} href={item.href} className="py-2 px-3 rounded related_link hover:bg-[#f2f2f2] dark:hover:bg-[#111827] flex items-center gap-3 group no-underline hover:no-underline border-b-0">
            {}
            <span className={`px-2 py-1 rounded-full text-xs font-semibold uppercase tracking-wide flex-shrink-0 ${getBadgeClass(item.badge)}`}>
              {item.badge}
            </span>

            {}
            <span className="text-base">{item.label}</span>
          </a>)}
      </div>
    </div>;
};

export const Link = ({href, children, className, ...props}) => {
  const localizedHref = localizeLink(href);
  return <a href={localizedHref} className={className} {...props}>
      {children}
    </a>;
};

This tutorial shows you how to build a recursive upload script that
combines <Link href="/guides/uploads/direct">direct uploads</Link>,
<Link href="/guides/uploads/chunked">chunked uploads</Link>, and
<Link href="/guides/uploads/check">preflight checks</Link> to handle
duplicate files, duplicate folders, and large files automatically.

<SignupCTA>
  A free Box developer account gives you a Developer Console, API access, and a test environment so you can upload entire folder trees to Box and build content workflows.
</SignupCTA>

<Info>
  The code samples in this tutorial use the
  <Link href="https://github.com/box/box-python-sdk">Box Python SDK (v10)</Link>.
</Info>

## Prerequisites

* A Box application with the <Link href="/guides/api-calls/permissions-and-errors/scopes/#read-and-write-all-files-and-folders">Read and write all files and folders stored in Box</Link> scope (`root_readwrite`) enabled
* Python 3.8 or later installed on your computer
* The Box Python SDK installed (`pip install "boxsdk~=10"`)
* An authenticated <Link href="/guides/authentication">Box client</Link>
* Completion of the <Link href="/guides/uploads/tutorials/upload-methods-python">Upload methods with Python</Link> tutorial, which defines the `file_upload` and `file_upload_chunked` helper functions this tutorial reuses to avoid duplicating upload logic

## Handle duplicate folders

Before you access a subfolder, check whether it already exists in Box.
If a folder with the same name exists, use the existing folder instead of
creating a new one:

```python Python v10 theme={null}
from box_sdk_gen import BoxAPIError, CreateFolderParent


def create_or_get_box_folder(client, folder_name, parent_folder_id):
    try:
        folder = client.folders.create_folder(
            folder_name, CreateFolderParent(id=parent_folder_id)
        )
    except BoxAPIError as err:
        if err.response_info.body.get("code") == "item_name_in_use":
            folder_id = err.response_info.context_info["conflicts"][0][
                "id"
            ]
            folder = client.folders.get_folder_by_id(folder_id)
        else:
            raise
    return folder
```

## Recursively upload a folder

Combine the helper functions into a single recursive function that walks the
local directory tree. For each item, the function:

1. Creates the folder in Box, or retrieves the existing folder, and then recurses into it.
2. Checks the file size to choose a direct or chunked upload. Files that are **20 MB or larger** use chunked upload; smaller files use direct upload.
3. Uploads a new version when a file with the same name already exists, by using the helpers from the upload methods tutorial.

```python Python v10 theme={null}
import pathlib

MIN_CHUNKED_SIZE = 20 * 1024 * 1024  # 20 MB


def folder_upload(
    client,
    box_base_folder_id,
    local_folder_path,
    min_chunked_size=MIN_CHUNKED_SIZE,
):
    local_folder = pathlib.Path(local_folder_path)
    for item in local_folder.iterdir():
        if item.is_dir():
            new_box_folder = create_box_folder(
                client, item.name, box_base_folder_id
            )
            folder_upload(client, new_box_folder.id, item, min_chunked_size)
        else:
            if item.stat().st_size < min_chunked_size:
                file_upload(client, str(item), box_base_folder_id)
            else:
                file_upload_chunked(client, str(item), box_base_folder_id)
```

The `min_chunked_size` parameter defaults to 20 MB, which is the minimum size for
<Link href="/guides/uploads/chunked">chunked uploads</Link>. You can
increase this threshold, but you cannot decrease it.

## Complete example

The following script authenticates a Box client, creates a destination
folder (or reuses an existing one), and recursively uploads the contents of a
local directory. It reuses the `create_box_folder` helper to get or create the
destination folder in the root folder (`"0"`):

```python Python v10 theme={null}
def main():
    client = get_authenticated_client()
    demo_folder = create_box_folder(client, "my-upload-folder", "0")
    folder_upload(client, demo_folder.id, "/path/to/local/folder")


if __name__ == "__main__":
    main()
```

## Resources

* <Link href="https://github.com/box-community/box-python-gen-workshop/blob/main/workshops/files/files.md">Box Python SDK workshop: files and uploads</Link>
* <Link href="https://github.com/box/box-python-sdk/blob/main/docs/uploads.md">Box Python SDK: Uploading a file</Link>
* <Link href="https://github.com/box/box-python-sdk/blob/main/docs/chunked_uploads.md">Box Python SDK: Chunked uploads</Link>

<RelatedLinks
  title="RELATED GUIDES"
  items={[
{ label: translate("Upload methods with Python"), href: "/guides/uploads/tutorials/upload-methods-python", badge: "GUIDE" },
{ label: translate("Upload All Files in Folder"), href: "/guides/uploads/chunked/folder", badge: "GUIDE" },
{ label: translate("Preflight Check"), href: "/guides/uploads/check", badge: "GUIDE" },
{ label: translate("Direct Uploads"), href: "/guides/uploads/direct", badge: "GUIDE" },
{ label: translate("Chunked Uploads"), href: "/guides/uploads/chunked", badge: "GUIDE" }
]}
/>
