Hi,
I have skimmed through most articles on PROC HTTP and have been trying to get this work for days now but without success.
I am using SAS 9.4 M7.
I need access token from a server where I have username and password. When I use Postman for this, it works without any problems:
https://www.dropbox.com/s/2jv7wdy4tcqzrkc/Postman.jpg?dl=0
It should be a simple matter of transferring the commands from Postman into SAS but nothing works for me. I must be missing something simple in the PROC HTTP syntax.
Here is an example of what I have tried:
Another example:
Here is a spec for the API:
https://www.dropbox.com/s/l36f1ij2gx5j2iv/API%20spec.jpg?dl=0
I will be extremely grateful for any help with this issue.
Thanks.
Here is the final, elegant solution that Chris did for me. It works like a charm.
filename resp temp;
/* must include content-type/CT= option */
proc http
url="https://serviceaddress/oauth/access_token"
in='grant_type=client_credentials&client_id=XXXXXXXX&client_secret=YYYYYYYY'
ct="application/x-www-form-urlencoded"
out=resp
method='POST'
;
run;
%put &=SYS_PROCHTTP_STATUS_CODE;
data _null_;
rc=jsonpp('resp','log');
run;
libname auth json fileref=resp;
data _null_;
set auth.root;
call symputx('token',access_token);
run;
%put &=token;
filename bldgs temp;
proc http
/* increase the number of records coming back to 1000*/
url="https://serviceaddress/api/buildings?number=1000"
method='GET'
ct='application/json'
out=bldgs;
headers "Authorization" = "Bearer &token.";
;
run;
%put &=SYS_PROCHTTP_STATUS_CODE;
libname bldg json fileref=bldgs;
data bldgs;
set bldg.data;
finYear = input(financialYearStart,yymmdd10.);
format finYear date9.;
run;
proc means data=bldgs;
var managementFee;
class finYear;
format finYear year.;
run;
This API implementation has been quite unforgiving, meaning that everything in the code had to be just right for it to work. Moreover, the error messages were not helpful, it would send the generic 500 server error for every wrong attempt.
Many thanks for your help Chris!
I do this all of the time from a variety of services. One difference from your Postman screenshot and your SAS code is that you are putting data on the URL (SAS) vs in the body (Postman). This is okay, but you want to make sure you're comparing the two equally.
proc http
url="https://serviceaddress/oauth/access_token" method='POST'
out=out
in="grant_type=client_credentials&client_id=XXXXX&client_secret=XXXXX&scope=SCOPES";
headerout=hdrs;
run;
If you take your working example in Postman and view its code (cURL version) and reply with that here (redacting any secrets), then we can help more.
Hi Chris,
Thanks for your amazingly swift reply.
I tried your code. I tried both, with and without SCOPE at the end. It feels like it's almost about to work but I am still getting 400 Bad request.
With client_id=123456.XXXXXXXX and client_secret=YYYYYYYYYYY, this is what gets stored in the 'Pout' file:
{"code":101,"title":"UnsupportedGrantType","details":"Grant type not supported: client_credentials123456.XXXXXXXX=123456.XXXXXXXXYYYYYYYYYYY=YYYYYYYYY"}
Here is the cURL code:
Thanks for your help again, much appreciated!
I think this is the PROC HTTP equivalent of that command.
filename temp out;
filename temp hdrs;
%let data = %sysfunc(urlencode(grant_type=client_credentials%str(&)client_id=123456.XXXXXXXX%str(&)client_secret=YYYYYYYYYYY));
proc http
url="https://serviceaddress/oauth/access_token" method='POST'
out=out
in="&data."
ct="application/x-www-form-urlencoded"
headerout=hdrs;
run;
/* report HTTP response code and content */
%put &SYS_PROCHTTP_STATUS_CODE.;
data _null_;
rc = jsonpp('out','log');
run;
Some REST API services are very picky -- you need to specify the content-type (CT= option here) and the data must be URL-encoded just so...even when it doesn't appear on the URL.
Here is the final, elegant solution that Chris did for me. It works like a charm.
filename resp temp;
/* must include content-type/CT= option */
proc http
url="https://serviceaddress/oauth/access_token"
in='grant_type=client_credentials&client_id=XXXXXXXX&client_secret=YYYYYYYY'
ct="application/x-www-form-urlencoded"
out=resp
method='POST'
;
run;
%put &=SYS_PROCHTTP_STATUS_CODE;
data _null_;
rc=jsonpp('resp','log');
run;
libname auth json fileref=resp;
data _null_;
set auth.root;
call symputx('token',access_token);
run;
%put &=token;
filename bldgs temp;
proc http
/* increase the number of records coming back to 1000*/
url="https://serviceaddress/api/buildings?number=1000"
method='GET'
ct='application/json'
out=bldgs;
headers "Authorization" = "Bearer &token.";
;
run;
%put &=SYS_PROCHTTP_STATUS_CODE;
libname bldg json fileref=bldgs;
data bldgs;
set bldg.data;
finYear = input(financialYearStart,yymmdd10.);
format finYear date9.;
run;
proc means data=bldgs;
var managementFee;
class finYear;
format finYear year.;
run;
This API implementation has been quite unforgiving, meaning that everything in the code had to be just right for it to work. Moreover, the error messages were not helpful, it would send the generic 500 server error for every wrong attempt.
Many thanks for your help Chris!
Save $250 on SAS Innovate and get a free advance copy of the new SAS For Dummies book! Use the code "SASforDummies" to register. Don't miss out, May 6-9, in Orlando, Florida.
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.