Upload
Last updated: Oct-27-2026
Using Cloudinary's upload capabilities, you can upload media assets in bulk with a variety of options for customizing how they will be uploaded.
When you upload to Cloudinary, your asset isn't only stored, but Cloudinary also automatically analyzes and saves important data about each asset, such as format, size, resolution, prominent colors, etc. This data is also automatically indexed to enable searching on those attributes.
Cloudinary provides a secure and comprehensive API for easily uploading media files from server-side code, directly from the browser or from a mobile application. For full details, see the Upload API reference. When needed, you can also use the API to perform unsigned uploads, but with a limited set of available upload parameters as a security precaution.
This guide provides an in-depth overview of Cloudinary's Upload API capabilities. To get started with the basics of uploading (and more) in 5 minutes or less, we recommend you first run through one of our backend SDK quick starts.
Each quick start gives you the code to configure your SDK, run your first upload, and then perform a few other common Cloudinary operations, all using your favorite programming language or framework.
If you haven't moved over your existing assets to Cloudinary yet, you may also want to check out our Migration guide.
Upload with Cloudinary MCP in Cursor
The Asset Management MCP server enables AI agents to upload images, videos, and raw files to your product environment and generate code that integrates Cloudinary functionality into your applications.
Learn more: Cloudinary MCP servers and LLM tools
Use this link to add the Asset Management MCP server to Cursor. Make sure to update your cloud name, API key, and API secret in Cursor Settings -> MCP Tools.
Add Asset Management MCP
Use this server to perform uploads, to delete, rename, or modify assets, to generate images, or to manage asset folders and tags.
Quick examples
Example 1: Upload the local hat.jpg image, and use the filename to set the public ID.
Example 2:
- Upload a remote image from wikipedia
- Set the public ID as "wiki_shirt"
- Request color and quality analysis
- Run the Google Auto Tagging add-on
- Automatically add the detected categories as tags to the asset
- Use signed uploads from your backend, and unsigned uploads (with an upload preset) for client-side uploads such as user-generated content.
- Configure upload presets instead of repeating upload options, and make sure the preset type (signed/unsigned) matches your upload type.
- Upload one high-quality original and transform on demand.
- Generate transformations during upload to warm the cache, so even the first request skips the processing delay.
- Use chunked uploads for large files.
- Generate metadata automatically during upload (tags, analysis, etc.).
- Moderate user-generated content before publishing.
These are some of the key upload recommendations. See Upload best practices for a complete list, details, and a code example for each practice.
Programmatic upload with the Node.js SDK video tutorial
Watch this video on how to quickly upload images, videos and other media files to Cloudinary using Cloudinary's Node.js SDK.
This video is brought to you by Cloudinary's video player - embed your own!
Use the controls to set the playback speed, navigate to chapters of interest and select subtitles in your preferred language.
Tutorial contents
Basic uploading
You can upload assets programmatically either by using authenticated uploads that include a signature, or using unauthenticated uploads without a signature but with certain restrictions for security reasons.
The upload API method enables you to upload files with a direct call to Cloudinary by sending an HTTPS POST request to the following Cloudinary REST API URL:
https://api.cloudinary.com/v1_1/<cloud name>/<resource_type>/upload
Where:
-
cloud nameis the name of your Cloudinary product environment. -
resource_typeis the type of file to upload. Valid values:image,raw,videoandautoto automatically detect the file type.
For example, to upload an image file to the Cloudinary 'demo' product environment, send an HTTPS POST request to the following URL:
https://api.cloudinary.com/v1_1/demo/image/upload
The contents of the POST request you send to Cloudinary depends on whether or not you are making an authenticated request or an unauthenticated request.
Uploading is performed synchronously, and once finished, the uploaded asset is immediately available for transformation and delivery.
- Use the auto-upload feature for lazy migration of all your assets from a remote location to Cloudinary with minimal effort on your side.
-
Enterprise customers can set up their account to use an EU or AP data center with the endpoints becoming
api-eu.cloudinary.comorapi-ap.cloudinary.comrespectively. - Audio files (e.g., MP3, WAV, FLAC) use the
videoasset type, notraw. This allows you to apply transformations like trimming, bitrate control, and format conversion. For details, see Uploading audio files.
Authenticated requests
Authenticated upload requests are performed over HTTPS using a secure protocol and include an authentication signature that's generated based on your product environment's cloud_name, api_key and api_secret parameters.
Upload with Cloudinary backend SDKs
Cloudinary's backend SDKs wrap the Upload endpoint and automatically generate the authentication signature based on the product environment credentials provided in your SDK configuration. When using an SDK to upload, the only required parameter is the file to upload.
For more details on uploading with SDKs, see the Upload page of the relevant backend SDK guide.
The Cloudinary upload SDK methods perform authenticated upload API calls over HTTPS using the following syntax:
For example, a simple upload of the sample.jpg file:
Upload via the REST upload endpoint
You can use Cloudinary's REST API directly for authenticated uploads. The Cloudinary upload endpoint performs an authenticated upload API call over HTTPS using the following syntax:
The Upload API supports multiple authentication methods:
- Basic Authentication: Authenticate using your API Key and API Secret via HTTP Basic Auth (no signature calculation required)
- Signature-based authentication: Manually generate a SHA signature with timestamp
api_secret in client-side code, regardless of which authentication method you use.Using Basic Authentication
Basic Authentication is the simplest way to authenticate Upload API requests from your backend. You provide your API Key and API Secret, and Cloudinary handles the authentication without requiring signature calculation.
To make an authenticated REST API upload request with Basic Authentication:
Add your credentials: Include your API Key and API Secret either in the URL or in an Authorization header.
-
Add the file parameter: Specify the file to upload. This can be:
- The actual data (byte array buffer)
- The Data URI (Base64 encoded)
- A remote FTP, HTTP or HTTPS URL of an existing file
- A private storage bucket (Amazon S3 or Google Cloud Storage) URL of an allowlisted bucket
For more details, see File source options.
-
Optionally, add customization parameters: Include any of the many optional upload parameters available for customizing your upload, such as:
- Naming parameters
- Tags and metadata (manually specified or automatically generated)
- Incoming transformations
- AI-based analysis options
See the optional upload parameters reference for the full list.
TipInstead of passing optional parameters directly in the upload call, you can define them in a signed upload preset and then pass that preset in your upload call.
Example with Basic Authentication:
Or using the Authorization header:
Using signature-based authentication
You can also authenticate using signature-based authentication, which requires manually generating the signature. See the Client-side uploading documentation for an example sample app to upload multiple files from the client-side while using a backend component to generate the signature.
To make an authenticated REST API upload request with signature-based authentication:
Add the file parameter: Specify the file to upload (see options above).
Add the api_key parameter: Include the unique API Key of your Cloudinary product environment.
Add the timestamp parameter: Specify Unix time in seconds of the current time (e.g., 1315060076).
Add the signature parameter: Generate a signature of all request parameters including the 'timestamp' parameter and any optional parameters added, but excluding the 'api_key', 'resource_type', 'cloud_name' and 'file' parameters, based on your product environment's API secret. The signature is valid for 1 hour. When no optional upload parameters are part of the signed payload, the string to sign is only the
timestampparameter pair.Optionally, add customization parameters: Include any of the optional upload parameters (see above).
Example with signature-based authentication:
Unauthenticated requests
Unauthenticated upload requests enable you to upload assets without the need to generate an authentication signature on your backend. However, for security reasons, only a small set of upload parameters can be passed directly in an unsigned upload call (see note below). Instead, most unsigned upload options are set via the unsigned upload preset that's passed in your unsigned upload call.
upload_preset) and can be triggered from any client that knows the preset name. Treat the preset name as sensitive. If you suspect the preset name was exposed:
- Create a new unsigned upload preset with a different name and update your clients to use it. See the Admin API: Create / Update / Delete upload presets.
- Delete any unauthorized assets that may have been uploaded (e.g., by tag or folder).
- Consider switching to authenticated (signed) uploads for stronger protection, especially in production.
An upload preset defines the upload options to apply to all assets that you upload with that preset specified. You can create multiple upload presets for different use cases. For more information on upload presets, see the upload preset guide.
Perform an unsigned upload with the SDKs
To perform an unsigned upload with the SDKs:
- Ensure your SDK is configured with your cloud name. For details, see the relevant backend SDK guide.
-
Call the unsigned_upload method: Use your SDK's
unsigned_uploadmethod with your file. - Set the upload_preset parameter: Specify the name of your unsigned upload preset.
For example, to upload the sample.jpg file with the unsigned_1 upload preset using an SDK:
Perform an unauthenticated REST API upload request
To make an unauthenticated upload request, include the following required parameters:
-
Add the file parameter: Specify the file to upload. This can be:
- The actual data (byte array buffer)
- The Data URI (Base64 encoded)
- A remote FTP, HTTP or HTTPS URL of an existing file
- A private storage bucket (Amazon S3 or Google Cloud Storage) URL of an allowlisted bucket
For more details, see File source options.
Add the upload_preset parameter: Specify the name of an unsigned upload preset that you defined for unsigned uploading.
For example:
Try out unsigned uploads in a sample app
If a supported parameter is included both in the unsigned upload call and in the unsigned upload preset, the upload preset value usually takes precedence, but there are some exceptions. For details, see Upload preset precedence.
Chunked asset upload
To support the upload of large files, the Cloudinary SDKs include a method which offers a degree of tolerance for network issues. The upload_large method uploads a large file to the cloud in chunks, and is required for any files that are larger than 100 MB. This is often relevant for video files, as they tend to have larger files sizes.
Upload a large file in chunks using an SDK
To upload a large file using the SDK 'upload_large' method:
Call the upload_large method: Use your SDK's
upload_largemethod (or equivalent) with your file.Specify the resource_type parameter: By default, backend SDKs set the
resource_typetoimage. Specify theresource_typeparameter to match your file type. For example, setresource_typetovideofor video files orautoif you don't know in advance what type to expect. For more details about theresource_typeoption, see Asset types.For files larger than 20 GB, set async to true: Add the
asyncparameter set totrueto upload the file asynchronously.Optionally, customize the chunk_size: The default chunk size is 20 MB, but you can set it as low as 5 MB using the
chunk_sizeparameter.
For example, uploading a large video file named my_large_video.mp4:
By default, the chunk size is set to 20 MB but can be set to as low as 5 MB by using the chunk_size parameter. For example, uploading a large video file named my_large_video.mp4 and setting chunk size to 6 MB:
- There are multiple responses to a chunked upload: one after each chunk that only includes basic information plus the
done : falseparameter, and a full upload response that's returned after the final chunk is uploaded withdone: trueincluded in the response. - The Node.js SDK has several different methods for uploading files, including some that take advantage of Node.js's stream functionality.
- If you have issues with large file uploads, see troubleshooting large file upload failures.
Manual chunked upload (REST)
While we recommend using a Cloudinary SDK for chunked uploads, you can also perform chunked uploads directly against the REST API. When uploading in multiple parts, make sure to:
- Include an
X-Unique-Upload-Idheader with a unique value for the upload, and use the same value for every chunk of that file. - Include a
Content-Rangeheader with the location of the chunk in the overall file: bytes<start>-<end>/<total>(for example, bytes0-5999999/22744222).- If the total size is unknown, set
to -1 for all but the last chunk.
- If the total size is unknown, set
- Use chunk sizes greater than 5 MB for all chunks except the last one.
- Send each chunk as a POST to the Upload API endpoint with your usual upload parameters. For example
Example 1: cURL
Chunk 1 (bytes 0-5,999,999 of a 22,744,222-byte file)
Chunk 2 (bytes 6,000,000-11,999,999 of a 22,744,222-byte file)
…repeat for subsequent chunks…
Final chunk (include the actual total)
Behavior: You’ll receive an intermediate response after each chunk (with done: false), and a final, full upload response after the last chunk (with done: true). See the SDK examples for additional patterns such as upload_chunked, upload_large, and controlling the chunk_size parameter.
Example 2: JavaScript fetch
Troubleshooting large file upload failures
If a large upload fails in the Media Library or via the Upload API, check the following:
- Upload size limits: Maximum total upload size (regardless of how its chunked) depends on your plan and account configuration. You can check your current usage limits in your Console Account Settings. For details on upload limits per plan, see Cloudinary pricing.
- Chunked uploads for files larger than 100 MB: Files larger than 100 MB must be uploaded in chunks when uploading via the API. The Media Library and Upload Widget handle chunked uploading automatically.
- Network stability: Large uploads are more sensitive to timeouts and interrupted connections. Retry the upload using a stable, high-bandwidth connection.
- Error responses and logs: Review the error returned in the Media Library or in the API response to identify whether the failure is related to file size limits, account limits, or an interrupted upload.
- Rate limits and storage quotas: Uploads can also fail if you reach a relevant account quota or API rate limit. See Account management and Programmable Media asset usage data. If you're unexpectedly reaching your account's limits when working with large files, you can contact support.
Upload response
A successful upload API call returns a response that includes the HTTP and HTTPS URLs for accessing the uploaded file, as well as additional information regarding the uploaded asset. Among these are the assigned public ID and current version of the asset (used in the Media Library, Admin API, and for building transformation and delivery URLs), the asset's dimensions, the file format and a signature for verifying the response. Depending on the optional parameters passed, the response might also include valuable analysis data such as detected faces, prominent colors, exif and other embedded metadata, quality/accessibility and other sophisticated media analysis data.
The exact keys returned in the response may vary depending on the parameters passed in your upload call. For details on these parameters and the response fields they influence, see the Upload API parameters table.
The following is an example of the JSON response returned:
Error handling
Once the POST request is received and processed by Cloudinary, the Upload API returns the status of requests using one of the following HTTP status codes:
- 200 - OK. Successful.
- 400 - Bad request. Invalid request parameters.
- 401 - Authorization required.
- 403 - Not allowed.
- 404 - Not found.
- 420 - Rate limited.
- 500 - Internal error. Contact support.
In the case of wrong usage of the API or an error, Cloudinary's response is returned with a non-OK (not 200) HTTP Status code and a body with the following JSON format:
Parallel uploads and rate limiting
Cloudinary’s Upload API accepts one file per request, but you can upload many files in parallel for faster migrations. Begin with a modest concurrency (for example, ~10 simultaneous upload requests) and increase gradually. If you hit account limits and receive HTTP 420 (Rate limited), reduce concurrency and retry the failed requests with exponential backoff.
Adjust the concurrency limit to your account and workload; if rate-limited, slow down and retry.
Upload considerations
There are various ways to upload your resources to your Cloudinary account. Cloudinary supports making both authenticated requests that require a signature generated on your backend, and unauthenticated requests with a restricted set of supported parameters.
For most production applications, we recommend:
- Using Cloudinary backend SDKs for server-side uploads.
- Using the Upload Widget for user-generated content.
- Using direct browser uploads when you want uploads to bypass your servers.
The following table summarizes the available upload options and the situations where each is most appropriate.
| Option | Description | Considerations |
|---|---|---|
| Cloudinary backend SDKs (Recommended for most applications) | The Cloudinary backend SDKs wrap the Upload API, including upload handling, signature generation, and response verification. | ✅ Significantly simplifies upload code compared to directly calling the REST API ✅ Automatically generates authentication signatures and validates responses ✅ Enables you to code in your chosen language ✅ Provides built-in support for uploading large files with chunked uploading |
| Upload widget (Recommended for accepting uploads from end users) | An interactive, feature-rich interface you can embed in your website or application to allow users to upload assets directly to Cloudinary. | ✅ No need to develop and maintain your own upload UI ✅ Simple to integrate ✅ Supports unauthenticated client-side uploads ✅ Enables uploads from social media and stock photo accounts |
| Direct upload from a browser (Recommended when uploads should bypass your servers) | The Cloudinary Backend SDKs can automatically add a file input field to your form that uploads files directly to Cloudinary, bypassing your own servers. | ✅ Uploads directly to your account without passing through your infrastructure ✅ Reduces server bandwidth and processing requirements 💡 Requires additional setup and configuration 💡 Requires the Cloudinary jQuery plugin |
| 'upload' endpoint of the REST API | The upload endpoint supports both authenticated and unauthenticated uploads and provides direct access to Upload API functionality. |
✅ Useful when coding in a language not covered by Cloudinary's SDKs ✅ Can be used for unauthenticated client-side requests 💡 Requires manually generating signatures and validating responses for authenticated uploads 💡 Requires more implementation and maintenance than using an SDK |
| The Cloudinary CLI | The Cloudinary CLI (Command Line Interface) enables you to interact with Cloudinary through the command line and provides additional features and helper commands. | ✅ Simple to use ✅ Useful for quickly uploading assets without setting up a formal coding environment ✅ Useful for experimenting with upload parameters and behavior ✅ Provides upload-specific helper functions (for example, sync) not directly available through other upload options |
| Lazy migration | Cloudinary's lazy migration with the auto-upload feature enables you to migrate files on demand from a remote location, where each asset is automatically uploaded to Cloudinary the first time its delivery URL is requested. | ✅ Simple to implement ✅ Only uploads the assets you actually need 💡 Not suitable if all assets must be migrated before a specific deadline |
| Media Library | The Media Library in the Cloudinary Console lets you upload assets using drag and drop or the built-in Upload Widget. | ✅ Simple to use ✅ Useful for quickly uploading assets without writing code ✅ Useful for experimenting with upload preset behavior 💡 Less suitable as a primary upload mechanism than programmatic approaches |
| Integrations | Cloudinary provides built-in integrations with many leading eCommerce, CMS, and PIM platforms. | ✅ Enables platform users to upload directly from the platform UI 💡 Requires initial setup and configuration by a platform administrator 💡 Less suitable as a primary upload mechanism outside the integrated platform |
| Media Library widget | The Media Library widget enables embedding Cloudinary Media Library capabilities, including uploads, into another application's UI. | ✅ Useful for implementing your own Cloudinary integration 💡 Less suitable as a primary upload mechanism at scale |
- MediaFlows, Cloudinary’s drag-and-drop workflow builder for image and video, offers the option to automate image upload with a low-code implementation. See MediaFlow’s documentation on media upload here.
- Usage limits for uploading, transforming and delivering files depend on your Cloudinary plan. For details, check the Account tab in your Cloudinary Console Settings.
Upload best practices
The Upload API supports hundreds of parameter combinations. The right configuration depends on your use case, but the following practices are recommended for most production environments.
Each example below sets its parameters directly in the upload call, so you can see exactly what the practice does. That's fine for a single upload; once the same settings apply to more than one upload, move them to an upload preset instead.
Ideally, use the Cloudinary Environment Config MCP server to create that preset, then apply it as shown below. There's no need to put preset-creation code inside your app.
Choose signed or unsigned uploads
Before uploading assets or creating upload presets, decide whether uploads should be signed or unsigned. This is the first decision every application (or AI agent) needs to make, since it determines how you authenticate and which upload options are available.
- Use signed uploads from your backend, where your API secret can generate an authentication signature. They support the full range of upload parameters and are the more secure option.
- Use unsigned uploads directly from the client side (browser or mobile app), such as user-generated content, where exposing your API secret isn't safe. They require an unsigned upload preset and support a restricted set of parameters.
Signed uploads from a mobile app still need a signature generated on your backend; the unsigned examples above are the direct client-side case.
Learn how: Authenticated requests, Unauthenticated requests
Build your upload pipeline around an upload preset
Rather than passing the same options in every request, define one or more upload presets as the primary configuration mechanism for your upload pipeline. A preset can define folder structure, naming conventions, metadata enrichment, transformations, moderation rules, access control, and more.
The examples below pass parameters in the upload call so each practice is visible on its own. Once two or more uploads share the same settings, move those settings onto a preset and pass upload_preset instead:
A preset is itself either signed or unsigned, so its signing mode must match your upload type: create a signed preset for signed uploads and an unsigned preset for unsigned uploads.
For unsigned uploads, the preset isn't just a convenience: only a restricted set of parameters can be sent in an unsigned request, so every other setting (moderation, metadata generation, eager transformations) has to come from the preset.
Create different presets for different asset types, such as product images, user-generated content, marketing assets, or videos, in the Cloudinary Console or with the Cloudinary Environment Config MCP server, which lets you describe the preset in natural language and reference it directly in your upload code. For example: "Create a signed upload preset for user-generated content that enables auto-tagging, applies moderation, and stores assets in the ugc folder."
For unsigned presets especially, set allowed_formats to restrict accepted file types. Anyone who knows an unsigned preset's name can upload through it, so restricting formats limits unexpected uploads and reduces the impact if the name is exposed.
Learn how: Upload presets
Upload once and transform on demand
Avoid uploading multiple copies of the same asset for different sizes or formats. Instead:
- Upload the highest-quality source asset
- Generate optimized delivery URLs dynamically, either on-the-fly or using eager transformations
- Deliver assets with automatic format and quality optimization
This reduces storage and simplifies asset management.
Learn how: Image transformations, Video transformations, Image optimization, and Video optimization
Generate eager transformations to warm the cache
If a transformation takes a long time to generate, generate it eagerly during upload instead of on the fly, so even the first request is served without the processing delay.
Examples include:
- Product thumbnails
- Gallery images
- Social sharing images
- Mobile-specific image sizes
- Common video renditions
- Transformations on long videos or that require heavy processing, such as AI analysis
Eager transformations reduce first-view latency, since the derived assets already exist by the time they're requested.
Set eager_async: true so the upload response isn't held up while the derived
assets generate, and use notification_url to learn when they're ready. A
given asset type usually needs the same renditions every time, so move these
onto its preset too.
Learn how: Eager transformations
Use chunked uploads for large files
Use a chunked upload method for files larger than 100 MB. Chunked uploads divide the file into smaller requests, improving reliability for large files and tolerance of unstable network connections.
Some SDKs provide a dedicated method for chunked uploads, for example Ruby's and Python's upload_large, Java's uploadLarge, and Node.js's upload_chunked, upload_chunked_stream, and upload_large. Others, such as the PHP and Go SDKs, use the regular upload method with the chunk_size option, which chunks the file automatically.
Set resource_type to match the file type, for example video for video files or raw for other file types. Some SDK methods default this, so check the SDK docs when choosing between methods.
The default chunk size is 20 MB; reduce it to as little as 5 MB with the chunk_size option. Smaller chunks may help on less reliable connections, while larger chunks reduce the number of requests.
For files larger than 20 GB, also set async to true. Maximum upload sizes depend on your plan and account configuration.
Chunked uploads return an intermediate response with done: false after each chunk; use the final response, with done: true, as the result of the upload.
chunk_size is a request-level option rather than a preset setting, so it stays in the upload call even when the rest of your configuration moves onto a preset.
Without an SDK, upload chunks manually with the REST API, including a unique X-Unique-Upload-Id header for the upload and a Content-Range header per chunk. See Chunked asset upload and the client-side chunked upload example for a complete implementation.
Learn how: Chunked asset upload
Automatically enrich assets with AI-generated metadata
If you plan to search, organize, moderate, or automate workflows based on asset content, generate metadata during upload rather than adding it later. Depending on your use case:
- Generate tags using auto-tagging services
- Run image analysis
- Extract colors and quality information
- Detect objects, faces, logos, or categories
This makes assets searchable and ready for downstream workflows as soon as they're uploaded.
Analysis settings are the same for every asset of a given type, and an unsigned upload can't send these parameters, so they're a natural fit for a preset. context usually varies per asset and stays in the upload call.
Learn how: Auto-tagging, Image analysis
Moderate user-generated content before publishing
For applications that accept user-generated content (UGC), moderation should be a standard part of the upload workflow. Depending on your requirements, it can be used to:
- Detect inappropriate images
- Flag potentially unsafe content
- Route uploads for manual review
- Prevent assets from being published automatically
Applying moderation at upload prevents unwanted content from becoming publicly available.
Moderated assets aren't publicly delivered until approved, so treat
moderation_status as part of your publishing logic rather than assuming an
uploaded asset is live. UGC is exactly the case where these settings belong on
a preset; if the upload comes straight from the browser, they have to, since
an unsigned request can't send them.
Learn how: Moderate assets
Transcribe and translate video and audio content during upload
For video or audio assets intended for a broad or global audience, generate transcripts, captions, and translations as part of the upload workflow rather than adding them later. Depending on your requirements:
- Generate automatic transcripts and closed captions
- Translate transcripts into multiple languages
- Add subtitles to improve accessibility and searchability
This makes video and audio content accessible and searchable as soon as it's uploaded.
Transcription runs asynchronously, so pair it with a notification_url rather
than waiting on the upload response. If every video should be transcribed, put
raw_convert on your video preset and used a signed upload; an unsigned upload can't send it at all.
Learn how: Video best practices
Apply access control during upload
If assets contain sensitive, licensed, or restricted content, configure access control during upload rather than applying it afterward. Examples include:
- Internal business assets
- User documents
- Licensed media
- Pre-release marketing content
This ensures assets are protected immediately.
Access control set at upload restricts delivery of the asset; your application still has to decide who is authorized before generating the signed URL. If a whole class of assets is restricted, set the delivery type on their preset rather than repeating it.
Learn how: Access-controlled assets
Store assets in meaningful folders
Use asset_folder values that reflect your application's structure, for example:
productsusersmarketingugcsupport
A clear folder structure simplifies asset management, permissions, automation, and lifecycle operations.
When a whole category of assets shares a folder, set asset_folder on that
preset; pass it in the call only when the folder varies per upload.
asset_folder isn't available on product environments using the legacy fixed folder mode. On those environments, use the folder parameter instead.Learn how: Asset folders
Use deterministic asset naming
When assets correspond to known business entities such as products, articles, or user profiles, use meaningful public IDs rather than randomly generated identifiers. Benefits include:
- Predictable URLs
- Easier integrations
- Simpler asset replacement workflows
- Easier debugging and administration
In an upload preset, set use_filename: true and unique_filename: false to generate consistent public IDs based on the uploaded file's name.
use_filename / unique_filename is the same for every upload in a pipeline,
so set it on the preset and let each call supply only the entity-specific
public_id.
Learn how: Public IDs
Avoid duplicate uploads
To prevent duplicate uploads:
- Use a deterministic
public_idwithoverwrite: falsewhen repeated uploads represent the same logical asset. - Use unique public IDs when different assets may have the same filename.
Learn how: Avoiding duplicate uploads, How can Cloudinary help me to avoid or detect duplicated uploads in my account?.
Automate post-upload workflows
If uploads trigger moderation, AI analysis, eager transformations, or other asynchronous processing, use notification URLs to automate downstream workflows rather than polling for results. Common use cases include:
- Updating application records when an upload completes
- Starting approval or review workflows
- Triggering AI analysis pipelines
- Synchronizing external systems
Your endpoint receives the result of each asynchronous step, so you can update records or start a review workflow instead of polling:
Learn how: Upload notifications and MediaFlows
Common upload patterns
The upload process is often the best place to automate tasks that would otherwise require additional processing later. The following examples show workflows and features that are frequently combined during upload.
| If you're uploading... | Consider adding... | Why |
|---|---|---|
| Product images | Eager background removal, auto-tagging | Creates cleaner storefront images and searchable product catalogs |
| User-generated images | Moderation, auto-tagging, structured metadata | Improves safety, discoverability, and content management |
| User profile photos | Face detection, moderation, eager avatar thumbnails | Produces optimized profile images and safer user experiences |
| Marketing campaign assets | Tags, structured metadata, asset folders | Makes assets easier to find and reuse across teams |
| E-commerce catalogs | Auto-tagging, metadata extraction, eager transformations | Accelerates search, merchandising, and delivery performance |
| Real-estate photos | Auto-tagging, quality analysis, eager responsive variants | Improves listing management and page performance |
| News and editorial images | Metadata extraction, tagging, eager social crops | Simplifies publishing workflows and social sharing |
| Brand assets | Structured metadata, folder organization, access control | Improves governance and asset consistency |
| Community uploads | Moderation, workflow automation, access control | Reduces operational overhead and content-review effort |
| Videos for web delivery | Eager video transcoding, thumbnails, AI analysis | Reduces playback delays and improves viewer experience |
| Training or educational videos | Automatic subtitles, transcripts, eager streaming formats | Improves accessibility and searchability |
| Searchable images and videos | Auto-tagging, color analysis, quality analysis | Enables richer search and filtering capabilities |
| Assets used in AI workflows | Auto-tagging, metadata extraction, structured metadata | Provides additional context for downstream AI applications |
Example: Product image workflow
A typical product image upload pipeline uploads the original, generates tags automatically, creates eager background-removed thumbnail derivatives, stores the asset in a product-specific folder, and attaches product metadata, all in a single upload call:
Every product upload needs this same configuration, so in practice you'd put
all of it except public_id and context on a products preset and call
upload with upload_preset plus those two values. The asset is then
immediately ready for search, merchandising, optimization, and delivery.
f_auto) doesn't work as expected in eager transformations. See Using automatic format in eager transformations.Example: User-generated content workflow
A typical user-generated content pipeline uploads the asset, runs automated moderation, generates tags, stores ownership metadata, and triggers a downstream review workflow:
If the upload comes directly from the browser rather than your backend, none of
these options can be sent in the request. Configure them on an unsigned upload
preset and upload with upload_preset: "<unsigned_preset_name>" instead.
Avoiding duplicate uploads
In many cases, you don’t need to explicitly check whether an asset exists before uploading it. Cloudinary provides upload-time mechanisms that can prevent duplicate uploads without requiring a separate existence check.
Both mechanisms require a deterministic public_id: the same value every time you upload a given logical asset. Assign it yourself, or set use_filename: true and unique_filename: false to derive it from the file's name. A random or auto-generated public_id never matches an existing one, so neither overwrite setting has any effect.
The behavior then depends on the overwrite setting:
overwrite: falsekeeps the existing asset if the samepublic_idis uploaded again. See Prevent duplicate uploads during upload.overwrite: truereplaces the existing asset if the samepublic_idis uploaded again. See Replacing existing assets.
The overwrite setting compares public IDs, not file contents. Two different files can use the same public ID, and the same file can be uploaded with different public IDs.
Recommended: prevent duplicate uploads during upload
When uploading the same logical asset more than once, use a deterministic public_id and set overwrite to false.
For example, every upload of the product image could use products/hat as its public ID. If an asset with that public ID already exists, Cloudinary skips the upload and the response includes:
This avoids an additional existence-check request and is usually the simplest and most scalable way to prevent repeated uploads.
Use unique public IDs for different assets
If different assets can have the same filename, don’t use the filename as the public ID and rely on overwrite to distinguish them.
For example, two users might both upload photo.jpg. If these are separate assets, give each one a unique public ID. You can:
- Leave
use_filenameunset so Cloudinary assigns a fully random public ID unrelated to the filename. - Set
use_filename: truetogether withunique_filename: trueso Cloudinary appends a random suffix to the filename-based public ID instead.
Otherwise, overwrite: false can cause the second upload to be skipped because an unrelated asset already uses the same public ID. overwrite: true would replace the existing asset.
Check whether an asset exists
If your workflow requires a separate existence check, you can use one of the following methods. These methods answer whether an asset is available or exists at a particular public ID; they don't detect whether two files have identical contents.
Option 1: Check existence via the delivery URL
You can issue an HTTP HEAD request to the asset’s delivery URL, for example:
- Returns 200 if the asset exists
- Returns 404 if the asset doesn't exist
- This method is fast and not rate-limited
- Results may be affected by CDN caching, especially if the asset was recently deleted
- Response headers count toward your bandwidth quota
- A HEAD request doesn't consume transformation quotas unless it triggers an auto-upload or fetch
- Returns 404 for authenticated or private assets too, so this method can't distinguish "doesn't exist" from "exists but isn't publicly accessible"
- This approach is suitable when performance is critical and eventual consistency is acceptable.
Option 2: Use the Admin API for an authoritative check
To definitively verify whether an asset exists, use the Admin API to retrieve the resource by its public ID.
If the asset exists, the API returns the resource and its data. If the asset doesn't exist, the API returns a 404 error.
- Results aren't affected by CDN caching
- Subject to Admin API rate limits
- If a deleted asset still has a backup available, the response includes a
placeholder: truefield.
Option 3: Using the Upload API explicit method (advanced)
Some upload pipelines use the Upload API’s explicit() method to check whether an asset exists before performing upload-related actions.
If the asset exists, the call returns the asset data. If it doesn't exist, the call returns an error.
- The Explicit method is primarily intended for applying actions to existing assets (such as eager transformations)
- This approach may be useful when your upload flow already relies on the Upload API and you require a definitive, non-cached result.
Detect duplicate or similar files
The methods above detect public ID conflicts. They don’t detect identical files uploaded with different public IDs.
To identify files after upload:
Compare the returned
etagvalues to identify exact matches.Request
phash: true, then compare the returned perceptual hashes to identify visually similar images.
These methods can help you clean up duplicates after upload. They don't prevent the upload itself.
For more information, see How can Cloudinary help me to avoid or detect duplicated uploads in my account?.
- Upload API Reference: Provides both REST and SDK syntax, parameter details, and examples for all methods of the Upload API.
- Upload Add-ons: Many of Cloudinary's add-ons can be activated by adding a parameter in your upload call. These add-ons enable you to take advantage of special deep-learning, AI, and other analytical capabilities offered by Cloudinary as well as other vision and image processing partners.
- Asset management: Covers options for managing your uploaded assets programmatically, including various CRUD options, backups and version management, notifications and webhooks, and authentication and signature options.
- User-generated content guide: Upload is a key component of allowing user-generated content to be displayed on your site. Learn about all the features you can take advantage of when handling user-generated content.