BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
smackerz1988
Pyrite | Level 9

I’m currently working on integrating the OpenAI API into SAS using PROC HTTP. I’m attempting to make a POST request to the Chat Completions API but I consistently encounter a rate limit error (HTTP 429) on every single attempt.

 

 

/* Body of the POST request */
filename in temp;
data _null_;
    file in;
    put '{"model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": "' "&FinalPrompt." '"}]}';
run;

/* Send the request */
filename resp temp;
proc http 
    method = "POST"
    url    = "https://api.openai.com/v1/chat/completions"
    ct     = "application/json"
    in     = in
    out    = resp;
    headers "Authorization" = "Bearer &api_key.";           
run;

/* Print the response */
data _null_;
    infile resp;
    input;
    put _infile_;
run;

I always receive the following error, regardless of how infrequent or spaced out the requests are:

 

HTTP 429: Rate limit exceeded

What I’ve Tried So Far:

  1. API Key: I’ve verified that my API key is working.
  2. Spacing Out Requests: Even when I run the requests manually with long delays in between, I still hit the rate limit on every attempt even though it is 3.5 and I have a paid subscription.
  3. Retry Logic: I attempted implementing retry logic with exponential backoff, but it hasn’t resolved the issue either.
1 ACCEPTED SOLUTION

Accepted Solutions
smackerz1988
Pyrite | Level 9

I think what the issue is that i haven't used this macro since mid 2023 and since then OpenAI API is much more restrictive now than it was then so I need to upgrade my OpenAI API account on top of my chatgpt paid subscription. As this code used to work fine as is. If anyone has a personal API_KEY you can put it as a %let statement at the start of this code and run this version of my  macro on your end to  and see if you get the same thing 

%let api_key = sk-XXXXXXXX
data Prompts;
    length code $ 16 description $ 1024;
    do code="optimize","clean","efficient","fast","readable"; 
        description="I want you to act as a code optimizer in SAS. {Describe problem with current code, if possible.} 
                     Can you make the code {optimize/clean/efficient/fast/readable}? {Insert Code}";
        output;
    end;
    code="explain"; 
    description="I want you to act as a code explainer in SAS. I don't understand this function. 
                 Can you please explain what it does, and provide an example? {Insert Code}";
	 output;
	 code="debug"; 
    description="I want you to be a SAS programmer, here is a piece of SAS code {Insert Code}
                 — I am getting the following error {Insert Error}. What is the reason for the bug?";
    output;
run;


%macro chatgpt(api_key=, print=true, out=, parameter=, insert_code=, describe_code=, insert_error=) / minoperator mindelimiter=' ';
    /* Use superq to avoid macro quoting issues */
    %let parameter = %superq(parameter);
    %let insert_code = %superq(insert_code);	 
    %let describe_code = %superq(describe_code);
	 %let insert_error = %superq(insert_error);
    %let out = %upcase(&out.);
    %let print = %upcase(&print.);

    /* Error checking */

    /* There must be an API key */
    %if(%bquote(&api_key.) =) %then %do;
        %put ERROR: No API key supplied.;
        %abort;
    %end;

    /* Print must be a valid value */
    %if(NOT (&print. IN TRUE FALSE YES NO 1 0 LOG)) %then %do;
        %put ERROR: Invalid value for print. Expected TRUE, FALSE, YES, NO, 1, 0, or LOG.;
        %abort;
    %end;

    /* Check that the Prompts dataset exists */
    %if NOT %sysfunc(exist(work.Prompts)) %then %do;
        %put ERROR: Dataset work.Prompts does not exist!;
        %goto EndOfMacro;
    %end;

    /* If the parameter is missing, show help information */
    %if %superq(PARAMETER) = %then %do;
        %put NOTE: This is a help info for macro &sysmacroname..;
        %put NOTE- The PARAMETER parameter allows only the following values for actions:;
        options nosource;
        data _null_;
            set Prompts;
            by description notsorted;
            if first.description then
                put "NOTE- " @;
            put code @;
            if last.description then 
                put " is for: " / @7 description/;
        run;
        options nosource;
        %goto EndOfMacro;
    %end;

    /* Check that the specified parameter is valid */
    options nosource nonotes;
    proc sql noprint;
        select code into :list_of_codes separated by " " from work.Prompts;
    quit;
    options source notes;

    %if NOT (%superq(PARAMETER) in (&list_of_codes.)) %then %do;
        %put ERROR: The PARAMETER parameter has an invalid value!;
        %goto EndOfMacro;
    %end;

    /* Print the prompt message */
    options nosource;
    data _null_;
        set Prompts;
        where code = symget('parameter');
        insert_code = symget('insert_code');
		  insert_error = symget('insert_error');
        describe_code = symget('describe_code');
        length Prompt $ 32767;
        Prompt = prxchange('s/\{Describe problem with current code, if possible\.\}/'||strip(describe_code)||'/io', -1, description);
        Prompt = prxchange('s/\{Insert Code\}/'||strip(insert_code)||'/io', -1, Prompt);
		  Prompt = prxchange('s/\{Insert Error\}/'||strip(insert_error)||'/io', -1, Prompt);
        Prompt = prxchange('s/\{optimize\/clean\/efficient\/fast\/readable\}/'||strip(code)||'/io', -1, Prompt);
        put "THE PROMPT:";
        put Prompt /;
    run;
    options nosource;

    /* If no output dataset is supplied, use __temp__ */
    %if(&out. =) %then %let outdata = __temp__;
    %else %let outdata = &out.;


    /* Body of the POST request */
    filename in temp;

  options nosource;
    data _null_;
    file in;
    put "{";
    put '"model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": ' """&parameter.""" '}, 
    {"role": "user", "content": ' """&insert_code.""" '},{"role": "user", "content": ' """&insert_error.""" '},
    {"role": "user", "content": ' """&describe_code.""" '}]';
    put "}";
    run;


    /* Reference that file as IN= parm in PROC HTTP POST */
    filename resp "%sysfunc(getoption(WORK))/echo.json";

    /* Send the request and payload */
    proc http 
        method = "POST"
        url    = "https://api.openai.com/v1/chat/completions"
        ct     = "application/json"
        in     = in
        out    = resp;
        headers "Authorization" = "Bearer &api_key.";
    run;    
    

    /* Check the status code */
    %if(&SYS_PROCHTTP_STATUS_CODE. NE 200) %then %do;
        %put An error occurred. HTTP &SYS_PROCHTTP_STATUS_CODE.: &SYS_PROCHTTP_STATUS_PHRASE;
        %abort;
    %end;
 
    /* Tell SAS to parse the JSON response */
    libname response JSON fileref=resp;

    /* Format JSON in presentable format */
    data &outdata. ;
        set response.choices_message;
            do row=1 to max(1,countw(content,'0A'x));
                outvar=scan(content,row,'0A'x);
                output;
            end;
        drop content;
    run;  

/*    libname response clear;*/

    /* Output to either ODS or the log */
    %if(&print. IN TRUE YES 1) %then %do;
        proc report data= &outdata. ;
            column outvar;
            define outvar / display "" style(column)=[cellwidth=6in fontsize=10pt asis=ON];
        run;
    %end;
        %else %if(&print. = LOG) %then %do;
            data _null_;
                 rc = jsonpp('resp','log');
            run;
        %end;

    /* Remove temporary data */
    %if(&out. =) %then %do;
        proc datasets lib=work nolist;
            delete &outdata.;
        quit;
    %end;
%EndOfMacro:
%mend;

%chatgpt(api_key=&api_key.,print=true, out=, parameter= explain, insert_code={proc fcmp});

 

View solution in original post

2 REPLIES 2
smackerz1988
Pyrite | Level 9

I think what the issue is that i haven't used this macro since mid 2023 and since then OpenAI API is much more restrictive now than it was then so I need to upgrade my OpenAI API account on top of my chatgpt paid subscription. As this code used to work fine as is. If anyone has a personal API_KEY you can put it as a %let statement at the start of this code and run this version of my  macro on your end to  and see if you get the same thing 

%let api_key = sk-XXXXXXXX
data Prompts;
    length code $ 16 description $ 1024;
    do code="optimize","clean","efficient","fast","readable"; 
        description="I want you to act as a code optimizer in SAS. {Describe problem with current code, if possible.} 
                     Can you make the code {optimize/clean/efficient/fast/readable}? {Insert Code}";
        output;
    end;
    code="explain"; 
    description="I want you to act as a code explainer in SAS. I don't understand this function. 
                 Can you please explain what it does, and provide an example? {Insert Code}";
	 output;
	 code="debug"; 
    description="I want you to be a SAS programmer, here is a piece of SAS code {Insert Code}
                 — I am getting the following error {Insert Error}. What is the reason for the bug?";
    output;
run;


%macro chatgpt(api_key=, print=true, out=, parameter=, insert_code=, describe_code=, insert_error=) / minoperator mindelimiter=' ';
    /* Use superq to avoid macro quoting issues */
    %let parameter = %superq(parameter);
    %let insert_code = %superq(insert_code);	 
    %let describe_code = %superq(describe_code);
	 %let insert_error = %superq(insert_error);
    %let out = %upcase(&out.);
    %let print = %upcase(&print.);

    /* Error checking */

    /* There must be an API key */
    %if(%bquote(&api_key.) =) %then %do;
        %put ERROR: No API key supplied.;
        %abort;
    %end;

    /* Print must be a valid value */
    %if(NOT (&print. IN TRUE FALSE YES NO 1 0 LOG)) %then %do;
        %put ERROR: Invalid value for print. Expected TRUE, FALSE, YES, NO, 1, 0, or LOG.;
        %abort;
    %end;

    /* Check that the Prompts dataset exists */
    %if NOT %sysfunc(exist(work.Prompts)) %then %do;
        %put ERROR: Dataset work.Prompts does not exist!;
        %goto EndOfMacro;
    %end;

    /* If the parameter is missing, show help information */
    %if %superq(PARAMETER) = %then %do;
        %put NOTE: This is a help info for macro &sysmacroname..;
        %put NOTE- The PARAMETER parameter allows only the following values for actions:;
        options nosource;
        data _null_;
            set Prompts;
            by description notsorted;
            if first.description then
                put "NOTE- " @;
            put code @;
            if last.description then 
                put " is for: " / @7 description/;
        run;
        options nosource;
        %goto EndOfMacro;
    %end;

    /* Check that the specified parameter is valid */
    options nosource nonotes;
    proc sql noprint;
        select code into :list_of_codes separated by " " from work.Prompts;
    quit;
    options source notes;

    %if NOT (%superq(PARAMETER) in (&list_of_codes.)) %then %do;
        %put ERROR: The PARAMETER parameter has an invalid value!;
        %goto EndOfMacro;
    %end;

    /* Print the prompt message */
    options nosource;
    data _null_;
        set Prompts;
        where code = symget('parameter');
        insert_code = symget('insert_code');
		  insert_error = symget('insert_error');
        describe_code = symget('describe_code');
        length Prompt $ 32767;
        Prompt = prxchange('s/\{Describe problem with current code, if possible\.\}/'||strip(describe_code)||'/io', -1, description);
        Prompt = prxchange('s/\{Insert Code\}/'||strip(insert_code)||'/io', -1, Prompt);
		  Prompt = prxchange('s/\{Insert Error\}/'||strip(insert_error)||'/io', -1, Prompt);
        Prompt = prxchange('s/\{optimize\/clean\/efficient\/fast\/readable\}/'||strip(code)||'/io', -1, Prompt);
        put "THE PROMPT:";
        put Prompt /;
    run;
    options nosource;

    /* If no output dataset is supplied, use __temp__ */
    %if(&out. =) %then %let outdata = __temp__;
    %else %let outdata = &out.;


    /* Body of the POST request */
    filename in temp;

  options nosource;
    data _null_;
    file in;
    put "{";
    put '"model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": ' """&parameter.""" '}, 
    {"role": "user", "content": ' """&insert_code.""" '},{"role": "user", "content": ' """&insert_error.""" '},
    {"role": "user", "content": ' """&describe_code.""" '}]';
    put "}";
    run;


    /* Reference that file as IN= parm in PROC HTTP POST */
    filename resp "%sysfunc(getoption(WORK))/echo.json";

    /* Send the request and payload */
    proc http 
        method = "POST"
        url    = "https://api.openai.com/v1/chat/completions"
        ct     = "application/json"
        in     = in
        out    = resp;
        headers "Authorization" = "Bearer &api_key.";
    run;    
    

    /* Check the status code */
    %if(&SYS_PROCHTTP_STATUS_CODE. NE 200) %then %do;
        %put An error occurred. HTTP &SYS_PROCHTTP_STATUS_CODE.: &SYS_PROCHTTP_STATUS_PHRASE;
        %abort;
    %end;
 
    /* Tell SAS to parse the JSON response */
    libname response JSON fileref=resp;

    /* Format JSON in presentable format */
    data &outdata. ;
        set response.choices_message;
            do row=1 to max(1,countw(content,'0A'x));
                outvar=scan(content,row,'0A'x);
                output;
            end;
        drop content;
    run;  

/*    libname response clear;*/

    /* Output to either ODS or the log */
    %if(&print. IN TRUE YES 1) %then %do;
        proc report data= &outdata. ;
            column outvar;
            define outvar / display "" style(column)=[cellwidth=6in fontsize=10pt asis=ON];
        run;
    %end;
        %else %if(&print. = LOG) %then %do;
            data _null_;
                 rc = jsonpp('resp','log');
            run;
        %end;

    /* Remove temporary data */
    %if(&out. =) %then %do;
        proc datasets lib=work nolist;
            delete &outdata.;
        quit;
    %end;
%EndOfMacro:
%mend;

%chatgpt(api_key=&api_key.,print=true, out=, parameter= explain, insert_code={proc fcmp});

 

smackerz1988
Pyrite | Level 9

Please let me know if you get the same message!. I'm writing a paper on this macro so need to make sure my code is still valid. 

sas-innovate-white.png

Missed SAS Innovate in Orlando?

Catch the best of SAS Innovate 2025 — anytime, anywhere. Stream powerful keynotes, real-world demos, and game-changing insights from the world’s leading data and AI minds.

 

Register now

How to Concatenate Values

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.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 2 replies
  • 1939 views
  • 3 likes
  • 1 in conversation