{"id":22320,"date":"2021-04-19T15:17:23","date_gmt":"2021-04-19T15:17:23","guid":{"rendered":"http:\/\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary"},"modified":"2023-09-19T14:25:27","modified_gmt":"2023-09-19T21:25:27","slug":"creating_an_api_with_python_flask_to_upload_files_to_cloudinary","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary","title":{"rendered":"Creating an API With Python Flask to Upload Files to Cloudinary"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p><a href=\"https:\/\/github.com\/rebeccapeltz\/flask-cld-upload\">Code<\/a><\/p>\n<p>Cloudinary offers SDKs for many programming languages and frameworks. Even though it also offers an Upload API endpoint for both back-end and front-end code, most developers find the SDKs very helpful. If you\u2019re working with a powerful back-end framework like Python Flask, you\u2019ll be happy to hear that a Python SDK is now available.<br \/>\nThis tutorial walks you through the process of building an API to upload images to Cloudinary. You can also upload other file types, including video and even nonmedia files, with the API.<\/p>\n<h2>Understanding the Difference Between Back End and Front End<\/h2>\n<p>Generally, code that runs on the server is <strong>back end<\/strong>, and code that runs on the browser is <strong>front end<\/strong>. However, since server code can render HTML, CSS,  and JavaScript, which all run on the browser, confusion sometimes results.\nIn the context of the Cloudinary SDKs, the back-end ones can read secret credentials, which should not be shared in the front end. In other words, you must never expose the back-end environment variables in the front end because front-end SDKs cannot hide credentials that are meant to be kept secret. As a solution, you can upload browser code with Cloudinary\u2019s <a href=\"https:\/\/cloudinary.com\/documentation\/upload_presets\"><strong>unsigned presets<\/strong><\/a> without revealing secret credentials. A better way would be to build a back-end API for secure uploads, keeping your <code>API_SECRET<\/code> credential hidden. Read on to see how to do that with Cloudinary\u2019s Python SDK and Python Flask or Python Django.<\/p>\n<h2>Coding a Flask API to Upload to Cloudinary<\/h2>\n<p>The Flask framework makes it easy to define routes and their functionalities. To get started, first create a route named <code>\/upload<\/code>, which accepts a <code>POST<\/code> that contains <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Methods\/POST\">multipart\/form-data<\/a>. You then package up the image file input into a <code>FormData<\/code> object in a submit handler and <code>POST<\/code> it to your own Flask API to call Cloudinary\u2019s Upload API, which is configured with your full set of Cloudinary credentials.<\/p>\n<p>Flask\u2019s <code>request<\/code> option enables you to get data from the client. When submitting files, such as uploading images, you can call upon <code>request<\/code> to access them.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">from<\/span> flask <span class=\"hljs-keyword\">import<\/span> Flask,render_template, request\n<span class=\"hljs-keyword\">if<\/span> request.method == <span class=\"hljs-string\">'POST'<\/span>:\n    file_to_upload = request.files&#91;<span class=\"hljs-string\">'file'<\/span>]\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>\nHowever, if you&#8217;re looking to simplify the process of building RESTful web services with Flask, you can utilize the <strong>flask_restful <\/strong>module. By creating a new class that inherits from <strong>Resource<\/strong>, you can define a<strong> post() <\/strong>method that accepts the file as a parameter. The <strong>werkzeug.datastructures.FileStorage<\/strong> class can be used to get the file object, which can then be saved to a desired location.\n<\/p>\n<p>\nHere&#8217;s an example of how you can integrate this approach:\n<\/p>\n<pre class=\"prettyprint\">from flask import Flask, request from flask_restful import Resource, Api from werkzeug.datastructures import FileStorage app = Flask(__name__) api = Api(app) class UploadFile(Resource): def post(self): file = request.files['file'] file.save('\/uploads\/' + file.filename) return 'File uploaded successfully!' api.add_resource(UploadFile, '\/upload')\n<\/pre>\n<p>\nThis code creates a new Flask project and an API endpoint that allows users to upload files. The <strong>post()<\/strong> method of the <strong>UploadFile <\/strong>class accepts the file as a parameter and saves it to the <strong>\/uploads<\/strong> directory. The return statement provides a success message. To test this code, tools like Postman can be used to send a POST request to the <strong>\/upload<\/strong> endpoint.\n<\/p>\n<p>The data retrieved from <code>request.files['file']<\/code> is an instance of <code>werkzeug.FileStorage<\/code>. The object can be handed off to the Python SDK\u2019s Upload function. Flask then wraps <a href=\"https:\/\/palletsprojects.com\/p\/werkzeug\/\">Werkzeug<\/a>, which handles the details of the Web Server Gateway Interface (WSGI).<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">if file_to_upload:\n    upload_result = cloudinary.uploader.upload(file_to_upload)\n    app.logger.info(upload_result)\n<\/code><\/span><\/pre>\n<p>For uploads to Cloudinary, the default <code>resource_type<\/code> is <code>image<\/code>. To expand or create a new Flask API, add <code>resource_type: 'video'<\/code> or <code>resource_type: 'raw'<\/code> for video or raw files, respectively.  <a href=\"https:\/\/cloudinary.com\/documentation\/upload_images#uploading_non_media_files_as_raw_files\">Raw<\/a> refers to nonmedia file formats, including text and JSON.<\/p>\n<p>Finally, <code>upload_result<\/code> is an object that contains the upload response. To complete the actions of your <code>upload<\/code> API, return that response to the client, like this:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">from<\/span> flask <span class=\"hljs-keyword\">import<\/span> jsonify\n<span class=\"hljs-keyword\">return<\/span> jsonify(upload_result)\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Below is the complete code of your <code>upload<\/code> API.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">@app.route(<span class=\"hljs-string\">\"\/upload\"<\/span>, methods=&#91;<span class=\"hljs-string\">'POST'<\/span>])\ndef upload_file():\n  app.logger.info(<span class=\"hljs-string\">'in upload route'<\/span>)\n\n  cloudinary.config(cloud_name = os.getenv(<span class=\"hljs-string\">'CLOUD_NAME'<\/span>), api_key=os.getenv(<span class=\"hljs-string\">'API_KEY'<\/span>), \n    api_secret=os.getenv(<span class=\"hljs-string\">'API_SECRET'<\/span>))\n  upload_result = None\n  <span class=\"hljs-keyword\">if<\/span> request.method == <span class=\"hljs-string\">'POST'<\/span>:\n    file_to_upload = request.files&#91;<span class=\"hljs-string\">'file'<\/span>]\n    app.logger.info(<span class=\"hljs-string\">'%s file_to_upload'<\/span>, file_to_upload)\n    <span class=\"hljs-keyword\">if<\/span> file_to_upload:\n      upload_result = cloudinary.uploader.upload(file_to_upload)\n      app.logger.info(upload_result)\n      <span class=\"hljs-keyword\">return<\/span> jsonify(upload_result)\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h2>Setting Up the Flask App<\/h2>\n<p>Next, follow the steps below to build the app.<\/p>\n<h3>Set Up the Virtual Environment<\/h3>\n<p>The command below establishes your virtual environment. This is an important step for encapsulating the libraries you\u2019ll be using in this app.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">python3 -m venv env\nsource env\/bin\/activate\n<\/code><\/span><\/pre>\n<p>To  deactivate the environment, type:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">deactivate\nsource env\/bin\/activate\n<\/code><\/span><\/pre>\n<h3>Install Flask<\/h3>\n<p>Install Flask with this command:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">python3 -m pip install Flask==1.1.1\n<\/code><\/span><\/pre>\n<h3>Add a <code>requirements.txt<\/code> File<\/h3>\n<p>Create a <code>requirements.txt<\/code> file to keep track of all the versioned libraries you need for the app and to facilitate future deployment.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">python3<\/span> <span class=\"hljs-selector-tag\">-m<\/span> <span class=\"hljs-selector-tag\">pip<\/span> <span class=\"hljs-selector-tag\">freeze<\/span> &gt; <span class=\"hljs-selector-tag\">requirements<\/span><span class=\"hljs-selector-class\">.txt<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h3>Upgrade the Python Package Installer<\/h3>\n<p>If necessary, upgrade the Python package installer, called PIP, with the command below. The command might vary, depending on your local Python installation. As shown below, the <code>freeze<\/code> command writes the library and version to the <code>requirements.txt<\/code> file.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">usr\/local\/opt\/python@3.9\/bin\/python3.9 -m pip install --upgrade pip\npython3 -m pip freeze &gt; requirements.txt\n<\/code><\/span><\/pre>\n<h3>Install Cloudinary<\/h3>\n<p>Next, install Cloudinary to gain access to its Upload API for the Python SDK.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"> <span class=\"hljs-selector-tag\">python3<\/span> <span class=\"hljs-selector-tag\">-m<\/span> <span class=\"hljs-selector-tag\">pip<\/span> <span class=\"hljs-selector-tag\">install<\/span> <span class=\"hljs-selector-tag\">cloudinary<\/span>\n <span class=\"hljs-selector-tag\">python3<\/span> <span class=\"hljs-selector-tag\">-m<\/span> <span class=\"hljs-selector-tag\">pip<\/span> <span class=\"hljs-selector-tag\">freeze<\/span> &gt; <span class=\"hljs-selector-tag\">requirements<\/span><span class=\"hljs-selector-class\">.txt<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h3>Add CORS support<\/h3>\n<p>If you want to access your <code>upload<\/code> API from a client served from a different host, add Cross Origin Resource Sharing (CORS) support:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">python3<\/span> <span class=\"hljs-selector-tag\">-m<\/span> <span class=\"hljs-selector-tag\">pip<\/span> <span class=\"hljs-selector-tag\">install<\/span> <span class=\"hljs-selector-tag\">flask-cors<\/span>\n<span class=\"hljs-selector-tag\">python3<\/span> <span class=\"hljs-selector-tag\">-m<\/span> <span class=\"hljs-selector-tag\">pip<\/span> <span class=\"hljs-selector-tag\">freeze<\/span> &gt; <span class=\"hljs-selector-tag\">requirements<\/span><span class=\"hljs-selector-class\">.txt<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Now you can add code to configure CORS for all the APIs and, specifically, the <code>upload<\/code> API. The code below demonstrates both options.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\">from flask_cors import CORS, cross_origin\napp = Flask(__name__)\n<span class=\"hljs-comment\"># After creating the Flask app, you can make all APIs allow cross-origin access.<\/span>\nCORS(app)\n<span class=\"hljs-comment\"># or a specific API<\/span>\n@app.route(<span class=\"hljs-string\">\"\/upload\"<\/span>, methods=&#91;<span class=\"hljs-string\">'POST'<\/span>])\n@cross_origin()\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h3>Work With Environment Variables<\/h3>\n<p>You can easily load environment variables with Python. Conveniently, the <code>python-dotenv<\/code> library is modeled on the Node.js <code>dotenv<\/code> package. You need three Cloudinary environment variables, i.e., <code>CLOUD_NAME<\/code>, <code>API_KEY<\/code>, and <code>API_SECRET<\/code>, for your code, but don\u2019t share <code>API_SECRET<\/code>.  You can export those variables to the local environment. When you deploy to Heroku, you can add them to make them available to the app when it runs in a Heroku container.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">from<\/span> dotenv <span class=\"hljs-keyword\">import<\/span> load_dotenv\nload_dotenv()\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In your <code>upload<\/code> API, you read in the environment variables with the <code>cloudinary.config()<\/code> function. You then access those variables with the <code>os.getenv()<\/code> function in the <code>dotenv<\/code> library.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">cloudinary.config(cloud_name = os.getenv(<span class=\"hljs-string\">'CLOUD_NAME'<\/span>), api_key=os.getenv(<span class=\"hljs-string\">'API_KEY'<\/span>), \n    api_secret=os.getenv(<span class=\"hljs-string\">'API_SECRET'<\/span>))\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>When working locally, you can create a gitignore\u2019d <code>.env<\/code> file that contains your Cloudinary credentials for local testing. The values for those credentials are displayed in your Cloudinary console.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">CLOUD_NAME=CLOUD_NAME\nAPI_KEY=API_KEY\nAPI_SECRET=API_SECRET\n<\/code><\/span><\/pre>\n<p>Next, install the <code>python-dotenv<\/code> library:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">python3<\/span> <span class=\"hljs-selector-tag\">-m<\/span> <span class=\"hljs-selector-tag\">pip<\/span> <span class=\"hljs-selector-tag\">install<\/span> <span class=\"hljs-selector-tag\">python-dotenv<\/span>\n<span class=\"hljs-selector-tag\">python3<\/span> <span class=\"hljs-selector-tag\">-m<\/span> <span class=\"hljs-selector-tag\">pip<\/span> <span class=\"hljs-selector-tag\">freeze<\/span> &gt; <span class=\"hljs-selector-tag\">requirements<\/span><span class=\"hljs-selector-class\">.txt<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h2>Testing the API Locally<\/h2>\n<p>You\u2019re now ready to test the app locally. You can do end-to-end testing with Postman and a local form that points at a server running on <code>localhost<\/code>.<\/p>\n<p>Create your app with an upload function by downloading, cloning, or copying from <a href=\"https:\/\/github.com\/rebeccapeltz\/flask-cld-upload\/blob\/master\/app.py\">this GitHub repository<\/a>. Add your credentials to a <code>.env<\/code> file and then start your server on <code>localhost<\/code>.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">python3<\/span> <span class=\"hljs-selector-tag\">app<\/span><span class=\"hljs-selector-class\">.py<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The above command opens a server at <code>http:\/\/127.0.0.1:5000\/<\/code>. Note that the default port is 5000.<\/p>\n<h3>Test With Postman<\/h3>\n<p>In Postman, do the following:<\/p>\n<ol>\n<li>Set the method to <code>POST<\/code>.<\/li>\n<li>Set the body type to <code>form-data<\/code>.<\/li>\n<li>Establish Key\/Value pairs for input. Set the value for the Key to <code>file<\/code>. Mouse over the <strong>Key<\/strong> field and choose <strong>Text<\/strong> or <strong>File<\/strong> from the drop-down menu. Under <strong>File<\/strong> is a <strong>Select Files<\/strong> button; click it to select a file from your local drive.<\/li>\n<li>Click <strong>Send<\/strong> to process the request.<\/li>\n<\/ol>\n<p>The Cloudinary Upload API response is displayed at the bottom of the page<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/image\/upload\/w_700,c_fill,f_auto,q_auto,dpr_2.0\/Web_Assets\/blog\/localpostman.jpg\" alt=\"Localhost with Postman\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"1091\"\/><\/p>\n<h3>Test With a Local Form<\/h3>\n<p>Follow these steps:<\/p>\n<ol>\n<li>\n<p>Open the <code>index.html<\/code> file with a local HTTP server. Choose a local file and click <strong>Upload<\/strong>.<\/p>\n<\/li>\n<li>\n<p>Run the JavaScript code below. The <code>fetch<\/code> command calls the server that runs on  <code>localhost<\/code>.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">fetch(<span class=\"hljs-string\">\"http:\/\/127.0.0.1:5000\/upload\"<\/span>, options)\n  .then(<span class=\"hljs-function\">(<span class=\"hljs-params\">response<\/span>) =&gt;<\/span> {\n    <span class=\"hljs-keyword\">return<\/span> response.json();\n  })\n  .then(<span class=\"hljs-function\">(<span class=\"hljs-params\">data<\/span>) =&gt;<\/span> {\n    <span class=\"hljs-built_in\">console<\/span>.log(data);\n  })\n  .catch(<span class=\"hljs-function\">(<span class=\"hljs-params\">error<\/span>) =&gt;<\/span> {\n    <span class=\"hljs-built_in\">console<\/span>.log(error);\n  });\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>If the process is successful, the Cloudinary Upload API response is displayed in the console.  Don\u2019t forget to set <code>event.preventDefault();<\/code> for the submit handler. Otherwise, you\u2019ll get a page refresh and won\u2019t see your log.<\/p>\n<\/li>\n<li>\n<p>Post a file in a submit handler by creating a <code>FormData<\/code> object and appending the file, as shown in the code below.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> formData = <span class=\"hljs-keyword\">new<\/span> FormData();\nformData.append(<span class=\"hljs-string\">\"file\"<\/span>, fileInput.files&#91;<span class=\"hljs-number\">0<\/span>]);\n<span class=\"hljs-keyword\">const<\/span> options = {\n  <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">\"POST\"<\/span>,\n  <span class=\"hljs-attr\">body<\/span>: formData,\n};\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<\/li>\n<\/ol>\n<p>The above code packages up the file you select in your form input as if you had submitted a multipart form. A multipart form is important for submitting files because each input is sent as a block of data, separated by boundaries.\nThe result is then displayed in your console, like this:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/image\/upload\/w_700,c_fill,f_auto,q_auto,dpr_2.0\/Web_Assets\/blog\/formlocal.jpg\" alt=\"local form\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"970\"\/><\/p>\n<h2>Deploying the Flask App to Heroku<\/h2>\n<p>Heroku is a platform as a service (PaaS) for serving prototypes or production-ready apps. Host the API you just built on Heroku by doing the following:<\/p>\n<ol>\n<li>\n<p>Create a free account on Heroku.<\/p>\n<\/li>\n<li>\n<p>Install <a href=\"https:\/\/gunicorn.org\/\">Gunicorn<\/a>, short for Green Unicorn, a Python WSGI HTTP server for hosting apps.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">python3 -m pip install gunicorn==20.0.4\npython3 -m pip freeze &gt; requirements.txt\n<\/code><\/span><\/pre>\n<\/li>\n<li>\n<p>Create a file called <code>Prodfile<\/code> with instructions for Heroku to start your app with Gunicorn:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">web<\/span>: <span class=\"hljs-selector-tag\">gunicorn<\/span> <span class=\"hljs-selector-tag\">app<\/span><span class=\"hljs-selector-pseudo\">:app<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<\/li>\n<li>\n<p>Create a file called <code>runtime.txt<\/code> that contains the Python version you want to use:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">python-3<\/span><span class=\"hljs-selector-class\">.9<\/span><span class=\"hljs-selector-class\">.1<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<\/li>\n<li>\n<p>Deploy with Git:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\">git init\ngit add .\ngit commit -m<span class=\"hljs-string\">\"my first commit\"<\/span>\n<span class=\"hljs-comment\"># Create a new Heroku app<\/span>\nheroku create\n<span class=\"hljs-comment\"># You will get a URL where the app will be hosted<\/span>\ngit remote -v\n<span class=\"hljs-comment\"># will confirm that you have set up a remote to push to Heroku<\/span>\n\n<span class=\"hljs-comment\"># If you have an existing Heroku app that you created in the Heroku GUI<\/span>\n<span class=\"hljs-comment\"># you can add it with this command instead of creating a new one<\/span>\nheroku git:remote -a thawing-inlet<span class=\"hljs-number\">-61413<\/span>\n\n<span class=\"hljs-comment\"># To deploy, you can now just push to Heroku<\/span>\ngit push heroku master\n\n<span class=\"hljs-comment\"># Open your server (it's nice to have a GET method so you can verify like this)<\/span>\nheroku open\n\n<span class=\"hljs-comment\"># To log in to Heroku from the command line, type:<\/span>\nheroku login\n<span class=\"hljs-comment\"># You\u2019ll then be prompted to open the webpage to log in<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<\/li>\n<\/ol>\n<p>You can build the new Heroku app with the CLI. However, as noted above, if you built it on the heroku.com dashboard, you can just add the link Heroku created for you to a <code>git:remote<\/code> command.<\/p>\n<h3>Loading Environment Variables on Heroku<\/h3>\n<p>To load environment variables on Heroku, open the dashboard and navigate to your new server instance. Click the <strong>Settings<\/strong> tab and scroll down to the <strong>Config Vars<\/strong> section. Click <strong>Reveal Config Vars<\/strong>, where you can enter Key\/Value pairs. This is where you load your Cloudinary environment variables: <code>CLOUD_NAME<\/code>, <code>API_KEY<\/code>, and <code>API_SECRET<\/code>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/image\/upload\/w_700,c_fill,f_auto,q_auto,dpr_2.0\/Web_Assets\/blog\/environmentvars.jpg\" alt=\"Heroku Config Vars\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"182\"\/><\/p>\n<p>For more options for setting up <code>prod<\/code> and <code>dev<\/code> instances, see the <a href=\"https:\/\/devcenter.heroku.com\/articles\/git\">Heroku documentation<\/a>.<\/p>\n<h2>Testing the API Deployed on Heroku<\/h2>\n<p>If you have a <code>GET<\/code> method API like the \u201cHello World!\u201d in the <code>app.py<\/code> example, you can open the Heroku app in the browser.<\/p>\n<p>For end-to-end testing, you can <code>POST<\/code> to the Heroku server link from Postman. The result is similar to that for local testing.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/image\/upload\/w_700,c_fill,f_auto,q_auto,dpr_2.0\/Web_Assets\/blog\/remotepost.jpg\" alt=\"Remote Post\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"1081\"\/><\/p>\n<p>Alternatively, modify the <code>index.html<\/code> file to post to your Heroku server. Just add your Heroku server link to the <code>fetch<\/code> command, like this:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">fetch(<span class=\"hljs-string\">\"https:\/\/xxxx.herokuapp.com\"<\/span>, options)\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h2>Securing the App<\/h2>\n<p>You might have noticed that the Heroku deployment link has been erased from the images above. That\u2019s for security. Deploying this app to Heroku enables you to upload data to Cloudinary. If the link is not secured for use on an authenticated webpage only, anyone can upload to your Cloudinary cloud. Even though you are hiding your environment variables, you\u2019d have created a public method to change data on your cloud.<\/p>\n<p>This tutorial does not cover how to set up authentication. Many options are available for authentication of API routes. You might want to look into Auth0, Google, or GitHub.  Alternatively, build your own process based on your user database for this type of authentication.<\/p>\n<h2>Moving On<\/h2>\n<p>Once you get this API running, you can build other media APIs with the Cloudinary Python SDK by following the same pattern. Have fun!<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":22321,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[263],"class_list":["post-22320","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-sdk"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Creating an API With Python Flask to Upload Files to Cloudinary<\/title>\n<meta name=\"description\" content=\"Learn how to build an API with Python Flask and Cloudinary&#039;s Python SDK to upload files of any format to Cloudinary.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Creating an API With Python Flask to Upload Files to Cloudinary\" \/>\n<meta property=\"og:description\" content=\"Learn how to build an API with Python Flask and Cloudinary&#039;s Python SDK to upload files of any format to Cloudinary.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2021-04-19T15:17:23+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-09-19T21:25:27+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1649718612\/Web_Assets\/blog\/Python_Flask_API_223210efa9\/Python_Flask_API_223210efa9-png?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"1540\" \/>\n\t<meta property=\"og:image:height\" content=\"847\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Creating an API With Python Flask to Upload Files to Cloudinary\",\"datePublished\":\"2021-04-19T15:17:23+00:00\",\"dateModified\":\"2023-09-19T21:25:27+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary\"},\"wordCount\":11,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718612\/Web_Assets\/blog\/Python_Flask_API_223210efa9\/Python_Flask_API_223210efa9.png?_i=AA\",\"keywords\":[\"SDK\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2021\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary\",\"url\":\"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary\",\"name\":\"Creating an API With Python Flask to Upload Files to Cloudinary\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718612\/Web_Assets\/blog\/Python_Flask_API_223210efa9\/Python_Flask_API_223210efa9.png?_i=AA\",\"datePublished\":\"2021-04-19T15:17:23+00:00\",\"dateModified\":\"2023-09-19T21:25:27+00:00\",\"description\":\"Learn how to build an API with Python Flask and Cloudinary's Python SDK to upload files of any format to Cloudinary.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718612\/Web_Assets\/blog\/Python_Flask_API_223210efa9\/Python_Flask_API_223210efa9.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718612\/Web_Assets\/blog\/Python_Flask_API_223210efa9\/Python_Flask_API_223210efa9.png?_i=AA\",\"width\":1540,\"height\":847},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Creating an API With Python Flask to Upload Files to Cloudinary\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Creating an API With Python Flask to Upload Files to Cloudinary","description":"Learn how to build an API with Python Flask and Cloudinary's Python SDK to upload files of any format to Cloudinary.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary","og_locale":"en_US","og_type":"article","og_title":"Creating an API With Python Flask to Upload Files to Cloudinary","og_description":"Learn how to build an API with Python Flask and Cloudinary's Python SDK to upload files of any format to Cloudinary.","og_url":"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary","og_site_name":"Cloudinary Blog","article_published_time":"2021-04-19T15:17:23+00:00","article_modified_time":"2023-09-19T21:25:27+00:00","og_image":[{"width":1540,"height":847,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1649718612\/Web_Assets\/blog\/Python_Flask_API_223210efa9\/Python_Flask_API_223210efa9-png?_i=AA","type":"image\/png"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary"},"author":{"name":"","@id":""},"headline":"Creating an API With Python Flask to Upload Files to Cloudinary","datePublished":"2021-04-19T15:17:23+00:00","dateModified":"2023-09-19T21:25:27+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary"},"wordCount":11,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718612\/Web_Assets\/blog\/Python_Flask_API_223210efa9\/Python_Flask_API_223210efa9.png?_i=AA","keywords":["SDK"],"inLanguage":"en-US","copyrightYear":"2021","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary","url":"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary","name":"Creating an API With Python Flask to Upload Files to Cloudinary","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718612\/Web_Assets\/blog\/Python_Flask_API_223210efa9\/Python_Flask_API_223210efa9.png?_i=AA","datePublished":"2021-04-19T15:17:23+00:00","dateModified":"2023-09-19T21:25:27+00:00","description":"Learn how to build an API with Python Flask and Cloudinary's Python SDK to upload files of any format to Cloudinary.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718612\/Web_Assets\/blog\/Python_Flask_API_223210efa9\/Python_Flask_API_223210efa9.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718612\/Web_Assets\/blog\/Python_Flask_API_223210efa9\/Python_Flask_API_223210efa9.png?_i=AA","width":1540,"height":847},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/creating_an_api_with_python_flask_to_upload_files_to_cloudinary#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Creating an API With Python Flask to Upload Files to Cloudinary"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718612\/Web_Assets\/blog\/Python_Flask_API_223210efa9\/Python_Flask_API_223210efa9.png?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/22320","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=22320"}],"version-history":[{"count":2,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/22320\/revisions"}],"predecessor-version":[{"id":31268,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/22320\/revisions\/31268"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/22321"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=22320"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=22320"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=22320"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}