Uploading files via API in Python

Hello all!

There are bits and pieces of documentation that relate to this topic, but none that explicitly show how to use this mutation from Python (that I’ve been able to find).

Basically, I’d like the query set up so that I have dynamic variables for both file (the image being uploaded) and item_id (the Item to which the image is uploaded to).

Does anyone have an example script showing how this is done? Here is what I’ve tried so far:

import requests
import json
import os
import pandas as pd
from pprint import pp

apiKey = os.getenv("MONDAY_API_TOKEN")
apiUrl = "https://api.monday.com/v2"
headers = {"Authorization" : apiKey}
url = "https://api.monday.com/v2/file"
payload = {'query': 'mutation add_file($file: File!, $item: ID!) {add_file_to_column (item_id: $item, column_id:"files__1" file: $file) { id } }'}
variables = {
    'file': open('/path/to/my/file.svg', 'rb'),
    'item_id': '6530743418'
}
payload['variables'] = variables
response = requests.post(url, headers=headers, data=payload)
pp(response.headers)

Appreciate any help.

Best,
-Kyle

There are two issues with your request (that I can see):

  • You need to make a multipart request, which typically will require a different syntax than a regular POST request.
  • You need to send three parts – a query, a map (which maps the ‘parts’ of your request to variables), and then the variables themselves (including the file).

You can refer to this working Postman example to see how the final HTTP requests should look.

From some Googling, looks like passing the files argument to requests.post will make it a multipart: How to send a "multipart/form-data" with requests in python? - Stack Overflow

So the syntax is something like this (pardon if my Python is rusty):

apiUrl = "https://api.monday.com/v2"
apiQuery = 'mutation add_file($file: File!, $item: ID!) {add_file_to_column (item_id: $item, column_id:"files__1" file: $file) { id } }'
apiMap = json.dumps({"image" : "variables.file", "item" : "variables.item_id"})# please check if this syntax is correct
payload = {
  'query': apiQuery, 
  'map': apiMap, 
  'file': open('/path/to/my/file.svg', 'rb'),
  'item': '6530743418'
}
response = requests.post(url, headers=headers, files=payload) # this is where you're using the 'files' argument to make a multipart request

@dipro Appreciate your response! I made some changes, but am still running into issues (receiving 500 Internal Server Error’s):

  1. I am using the /file API, not the base one.
  2. I changed the item key of payload to match the value of the corresponding key-value pair in apiMap.
  3. I set the correct file path to the file I want to upload (not including here for security)
url = "https://api.monday.com/v2/file" 
apiQuery = 'mutation add_file($file: File!, $item: ID!) {add_file_to_column (item_id: $item, column_id:"files__1" file: $file) { id } }'
apiMap = json.dumps({"image" : "variables.file", "item" : "variables.item_id"})
payload = {
  'query': apiQuery, 
  'map': apiMap, 
  'file': open('/path/to/my/file.svg', 'rb'),
  'item_id': '6530743418'
}
response = requests.post(url, headers=headers, files=payload) 
print(response.status_code)
print(response.reason)

The add_file mutation is a bit fussy and will return a 500 status more often than the other ones…

Anyway, looking at the above, your map is wrong – the parts of the HTTP form should correspond to the left-hand-side of the map JSON.

So if your map is:

{ "image" : "variables.file" }

The API will expect to get an HTTP part called “image”. In your code above, it gets “file”.

Try one of these instead:

url = "https://api.monday.com/v2/file" 
apiQuery = 'mutation add_file($file: File!, $item: ID!) {add_file_to_column (item_id: $item, column_id:"files__1" file: $file) { id } }'
apiMap = json.dumps({"image" : "variables.file", "item" : "variables.item_id"})
payload = {
  'query': apiQuery, 
  'map': apiMap, 
  'image': open('/path/to/my/file.svg', 'rb'),
  'item': '6530743418'
}
response = requests.post(url, headers=headers, files=payload) 
print(response.status_code)
print(response.reason)

Or perhaps better…

url = "https://api.monday.com/v2/file" 
apiQuery = 'mutation add_file($file: File!, $item: ID!) {add_file_to_column (item_id: $item, column_id:"files__1" file: $file) { id } }'
apiMap = json.dumps({"file" : "variables.file", "item_id" : "variables.item_id"})
payload = {
  'query': apiQuery, 
  'map': apiMap, 
  'file': open('/path/to/my/file.svg', 'rb'),
  'item_id': '6530743418'
}
response = requests.post(url, headers=headers, files=payload) 
print(response.status_code)
print(response.reason)

If this doesn’t work, it’s worth opening a ticket at this form so we can get your account info and look up error logs on our side!

Appreciate the follow up @dipro . Your code did not work, but I’d like to provide some code that did and maybe you can help me expand from there. The following (based on the Python-Requests code snippet provided by the Postman example here), did in fact work:

import requests
import json
import os
import pandas as pd

apiKey = os.getenv("MONDAY_API_TOKEN")
headers = {"Authorization" : apiKey, 'API-version':'2023-10'}
url = "https://api.monday.com/v2/file"
payload = {
    'query': 'mutation add_file($file: File!) {add_file_to_column (item_id: 6530743418, column_id:"files__1" file: $file) {id}}',
    'map': '{"image":"variables.file"}'
    }
files=[
  ('image',('file',open('/path/to/my/file.svg','rb'),'application/octet-stream'))
]


response = requests.post(url, headers=headers, data=payload, files=files)

print(response.text)
print(response.reason)

Now, I just need some guidance on how to also pass item_id as a dynamic variable argument to the query. I know that the query itself would probably look something like:

mutation add_file($file: File!, $item_id: ID!) {add_file_to_column (item_id: $item_id, column_id:"files__1" file: $file) {id}}

but it is unclear to me where to put the Python variable containing the item_id so that it gets interpreted properly.

Appreciate your help.

1 Like

Ah great, glad you got something working.

If you want to add the item ID as a variable, add it to three places:

  • The query
  • The map – in the format { "key_in_http_form" : "variables.variable_name_in_query}
  • As a key in the form (aka the payload variable above)
payload = {
    'query': 'mutation add_file($file: File!, $item_id:ID!) {add_file_to_column (item_id: $item_id, column_id:"files__1" file: $file) {id}}',
    'map': '{"image":"variables.file", "item" : "variables.item_id"}',
    'item' : 6530743418
    }
files=[
  ('image',('file',open('/path/to/my/file.svg','rb'),'application/octet-stream'))
]


response = requests.post(url, headers=headers, data=payload, files=files)
1 Like

Thanks so much @dipro! Appreciate your help, this worked wonderfully. I have a few follow-up questions.

One thing that’s unexpected:

When I navigate to the Board/Item of interest, the ‘files’ column does not actually show the image preview, but instead a file attachment icon, with options to download/etc.

image

Please tell me there is a way to display the image itself. Seems to be a previously identified issue.

Also, does the API support giving a name to the file? It seems it does not use the original filename, and something a bit more descriptive than file would be preferred.

Thanks again

Hello there @kyleweise what type of file is that?

There is no way to select a name for the file when uploading it via API.

Looking forward to hearing from you!

Cheers,
Matias

Hello @Matias.Monday!

It is a .svg image file. The other issue I linked to seems to have the same behavior with pdf’s (although it didn’t look like they were using the API), so I’m not sure it’s a file type-specific problem.

Hello again @kyleweise,

I could not reproduce the issue.

Would you be able to please fill this form (https://support.monday.com/hc/en-us/requests/new?ticket_form_id=13855862562962) adding as much information as possible to it (such as account ID, board IDs, item IDs, timestamps, etc.) so that our team can take a look into it?

Looking forward to hearing from you via the form!

Cheers,
Matias

For completeness, this is the what the Technical Support Engineer sent me which helped me solve my issue:

import requests 
import json 
import os 
import pandas as pd 
apiKey = "token" 
headers = {"Authorization" : apiKey, 'API-version':'2023-10'} 
url = "https://api.monday.com/v2/file" 
payload = { 
    'query': 'mutation add_file($file: File!) {add_file_to_column (item_id: 123456789, column_id:"files__1" file: $file) {id}}', 
    'map': '{"image":"variables.file"}' 
    } 
files=[ 
  ('image',('test.png',open('test.png','rb'),'image/png')) 
] 
response = requests.post(url, headers=headers, data=payload, files=files) 
print(response.text) 
print(response.reason)

I needed to:

  • set the header correctly: image/png, not application/octet-stream
  • specify the file type both in the name of the file: test.png, and the actual file itself: open('test.png', 'rb')

Thank you @kyleweise for sharing this!