Using the API in SAS

I hadn't seen any examples of SAS code using the Census API yet, so I wanted to share my piece of code. This code only works with SAS9.4M4 or higher.

The example code underneath creates a table with the number of veterans for all tracts in New York State.

--- SAS Code ---

filename resp temp;
%let myCensusKey = [CensusAPIKey]; /*use your API key here*/

%macro CensusAPIresponseHeaders(responseFile, outTable);

/*puts the results from a responsefile into a table and uses the first row from the response to create variable names */

libname temp JSON fileref=&responseFile;

data _null_;
  set temp.root (obs=1);
  array elemCols{*} element:;
  length rename $4000;
  do i=1 to dim(elemCols);
    rename=catx(' ',rename, catx('=','element'||compress(put(i,3.)),elemCols{i}));
  end;
  call symputx('rename',rename);
run;

data &outTable;
  set temp.root (firstobs=2);
  rename &rename;
run;

libname temp clear;
%mend;

proc http
  url="api.census.gov/.../acs5
  method= "GET"
  out=resp;
run;

%CensusAPIresponseHeaders(resp,ny_vets);

  • Here is an alternate for those lacking the JSON engine (SAS versions before v9.4M4). It strips the brackets from the start and end of each row returned from the API, which makes it as simple CSV file. Then it converts it to a SAS dataset using PROC IMPORT.
    Just replace the URL in the "FILENAME ACS_API ..." line below with the URL of your request, run the code, and you'll got your data in as SAS dataset colled MyNewSASDataset.

    %let myCensusKey = [CensusAPIKey]; ** use your API key here ;

    ** Specify the API URL for the data you want. ;
    FILENAME ACS_API URL
    "api.census.gov/.../acs5 ;

    FILENAME _NoBrkts TEMP ; ** Temporary file, in which to save the Census API text, after stripping the leading and closing bracket(s) on each row. ;

    ** Strip the leading and trailing bracket(s) from each row that the API returns. That turns the file into a simple CSV file. ;
    DATA _NULL_ ;
    LENGTH _NoBrkts $ 1000. ; ** Make sure that this length is long enough to capture the longest row produced by the API call. ;
    INFILE ACS_API ; ** Read the API output. ;
    INPUT ; ** Read in a row. ;
    FILE _NoBrkts ; ** Direct the subsequent PUT to the temporary text file. ;
    _NoBrkts = PRXCHANGE( "s/^\s*\[+|\]+\s*$//", -1, _INFILE_) ; ** Strip the leading and trailing bracket(s) from the row. ;
    PUT _NoBrkts ; ** Write the revised line to the temporary text file. ;
    RUN ;

    PROC IMPORT DATAFILE=_NoBrkts DBMS=CSV OUT=MyNewSASDataset REPLACE ; ** Now just import the revised file as a .csv file. ;
    GETNAMES=YES ; ** Get the variable names from the 1st line of the file. ;
    RUN ;
  • I have very much appreciated this macro and have used it repeatedly. 

    I wanted to pass along that I have made a couple amendments to deal with issues that have arisen.

    The first issue is that it is easy to get the Name variable more than once in an API call, so I added a line that prevents a repeat of that variable name from crashing the program. 

    The second is that some variable names in the json had a space in them, like "county subdivision" and that was breaking the rename statement. I just put a compress() to handle that. 

    The third is that some tables now have more than 1,000 cells. The existing macro has a put (i,3.) that broken when the variable "i" got above 999.  I just switched it to put(i,4.). 

    And the fourth, also related to wide tables (example subject table S1501 with over 1,500 variables), the rename variable in the macro was being truncated, even when I made the length 32000. So I now test to see if the rename variable is full and write overflow to a second rename variable. 

    Here is the revised macro.  Once again, I owe great thanks for the creation of this. I hope my amendments can help others get value from it, as well. 

    --- SAS Code ---

    filename resp temp;
    %macro CensusAPIresponseHeaders(responseFile, outTable);


    /*puts the results from a responsefile into a table and uses the first row from the response to create variable names */

    libname temp JSON fileref=&responseFile;

    data _null_;
    set temp.root (obs=1);
    array elemCols{*} element:;
    length rename1 rename2 $32000;
    do i=1 to dim(elemCols);
    if elemCols(i) eq 'NAME' then elemcols(i) = strip(elemCols(i)) || put(i,z4.);* fix where 'name' is reused as a variable name in json output;
    if length(rename1) le 31000 then rename1=catx(' ',rename1, catx('=','element'||compress(put(i,4.)),compress(elemCols{i})));* fix if column has space, ie "county subdivision";
    if length(rename1) gt 31000 then rename2=catx(' ',rename2, catx('=','element'||compress(put(i,4.)),compress(elemCols{i})));* fix if column has space, ie "county subdivision";
    end;

    ;* testing;
    ;* rename_len1 = length(rename1);
    ;* rename_len2 = length(rename2);
    ;* put rename_len1 =;
    ;* put rename_len2 =;
    call symputx('rename1',rename1);
    call symputx('rename2',rename2);
    run;

    data &outTable;
    set temp.root (firstobs=2);
    rename &rename1. &rename2.;
    run;

    libname temp clear;
    %mend;