HTTP400 when making POST query via MATLAB

I’d like to use MATLAB to pull xrd data from the API with a pretty_formula criteria. I can successfully get this data using the requests.post command in python as described on the mapidoc github readme. However, I’m unable to replicate this in MATLAB, which is used by everyone in my lab. I am receiving HTTP400 responses no matter what I have tried.

Attempt 1

url = 'https://www.materialsproject.org/rest/v2/query';
api_key = #########; %my api key goes here

criteria = struct('pretty_formula', 'PbCl2');
data = struct('criteria', criteria, 'properties', 'xrd.Cu');

options = weboptions('KeyName', 'X-API-KEY', 'KeyValue', api_key, 'RequestMethod', 'post', 'MediaType', 'application/json');

r = webwrite(url, data, options)

This returns status 400 “Bad Request”. As far as I can tell, webwrite() does not provide any detailed traceback to diagnose the 400 further.

Attempt 2

url = 'https://www.materialsproject.org/rest/v2/query';
api_key = ##########; %my api key goes here

headerField = matlab.net.http.field.ContentTypeField('application/x-www-form-urlencoded');
headerField = addFields(headerField, matlab.net.http.HeaderField('X-API-KEY', api_key))

criteria = struct('pretty_formula', 'PbCl2')
data = struct('criteria', criteria, 'properties', 'xrd.Cu')
body = matlab.net.http.MessageBody(jsonencode(data));

request = matlab.net.http.RequestMessage(method, headerField, body);
r = send(request, url, options)

which returns an ‘HTTP/1.1 400 Bad Request’ error with the traceback

    'Traceback (most recent call last):
       File "/opt/miniconda3/envs/mpprod3/lib/python3.6/site-packages/django/utils/datastructures.py", line 77, in __getitem__
         list_ = super().__getitem__(key)
     KeyError: 'criteria'
     
     During handling of the above exception, another exception occurred:
     
     Traceback (most recent call last):
       File "/var/www/python/matgen_prod/materials_django/rest/rest.py", line 94, in wrapped
         d = func(*args, **kwargs)
       File "/var/www/python/matgen_prod/materials_django/rest/rest.py", line 204, in query
         criteria = json.loads(post["criteria"])
       File "/opt/miniconda3/envs/mpprod3/lib/python3.6/site-packages/django/utils/datastructures.py", line 79, in __getitem__
         raise MultiValueDictKeyError(key)
     django.utils.datastructures.MultiValueDictKeyError: 'criteria'

Any ideas on how to work through this? If we can figure this out I believe it would be a helpful addition to the documentation, as I imagine many users would be interested in performing more advanced queries from MATLAB.

1 Like

Hi @RishiEKumar,

I did some poking around. The short answer to your question is that you need to treat “criteria” and “properties” as fields whose data are JSON-serialized strings:

url = 'https://www.materialsproject.org/rest/v2/query';
api_key = '###########';

criteria = struct('pretty_formula', 'PbCl2');
props = 'xrd.Cu';

options = weboptions('KeyName', 'X-API-KEY', 'KeyValue', api_key, ...
'RequestMethod', 'post', 'Debug', true);     % 'Debug': true outputs extra info

r = webwrite(url, 'criteria', jsonencode(criteria), ...
    'properties', jsonencode(props), options)

I had to back out the request from the Python interface by doing:

import requests
import json
data = {
    'criteria': {'pretty_formula': 'PbCl2'},
    'properties': 'xrd.Cu'
}
response = requests.post(url, headers={'X-API-KEY': api_key},
    data={k: json.dumps(v) for k, v in data.items()})    # This is where the values of the fields are converted to JSON strings
print(response.request.body)    # You can explore the constructed request from the response
# 'criteria=%7B%22pretty_formula%22%3A+%22PbCl2%22%7D&properties=%5B%22xrd.Cu%22%5D'

I will pass along your suggestion to the team to improve the documentation to make this more clear in case other people are using packages other than Python to make the API request.

Best,
Christian

2 Likes