3  Testing API Connection

Many of the interactive portions of this workshop depend on successfully being able to connect to a generative AI model via API (we will discuss API keys in more depth later). For this workshop you’ll need to obtain your own Anthropic API key through console.anthropic.com.

$5 in credit should be sufficient for all workshop purposes, and you’ll have enough left over to continue practicing sending LLM calls via API..

The following is a basic function for you to test that you can connect to Anthropic’s Claude Sonnet 4.6 model. We’ll cover some of the elements of the function later. To use this function you’ll need to have the API loaded for the R session, which can you do via the syntax below:

Code
library(httr)
library(jsonlite)
Sys.setenv(ANTHROPIC_API_KEY = rstudioapi::askForSecret("Anthropic API Key"))

When using the API key in your workflow, you can skip this step by creating an .Renviron file that is saved in your working directory. Each API key should have a line like: ANTHROPIC_API_KEY='paste_your_api_key_here'.

3.1 call_claude function

You can download the following function by clicking the copy button (looks like a clipboard) in the code chunk displayed below. Alternatively, you can click the icon below:

📥 Download call_claude_NCME26

Code
library(httr)
library(jsonlite)

call_claude <- function(prompt,
                        model = "claude-sonnet-4-6",
                        system = NULL,
                        temperature = 0.5,
                        max_tokens = 4096,
                        effort = "low") {
  
  # Get API key from environment
  api_key <- Sys.getenv("ANTHROPIC_API_KEY")
  
  # Convert text prompt to required message format
  messages <- list(list(role = "user", content = prompt))
  
  # Build request body
  request_body <- list(
    model = model,
    messages = messages,
    max_tokens = max_tokens,
    temperature = temperature,
    output_config = list(effort = effort)
  )
  
  # Add system prompt if provided
  if (!is.null(system)) {
    request_body$system <- system
  }
  
  # Set up headers
  headers <- add_headers(
    "x-api-key" = api_key,
    "anthropic-version" = "2023-06-01",
    "content-type" = "application/json"
  )
  
  # Make the API request
  response <- POST(
    url = "https://api.anthropic.com/v1/messages",
    headers,
    body = toJSON(request_body, auto_unbox = TRUE)
  )
  
  # Check if request was successful
  if (http_status(response)$category != "Success") {
    stop(paste("API request failed:", http_status(response)$message,
               "\nDetails:", content(response, "text", encoding = "UTF-8")))
  }
  
  # Parse response and extract text content
  result <- fromJSON(content(response, "text", encoding = "UTF-8"))
  return(result$content$text)
  
}

The only argument that needs to be supplied to the function is the prompt that you want to send to the model; all other arguments have default values, and we’ll discuss the other options in more detail in subsequent sections. The prompt should be a text string that is enclosed by parentheses: e.g., “Tell me a joke about educational measurement.”

Code
test_joke <- call_claude("Tell me a joke about educational measurement.")

test_joke
[1] "Here's one:\n\nA psychometrician walks into a bar and orders a drink. The bartender asks, \"How was your day?\"\n\nHe says, \"Well, I can't be certain, but I'm 95% confident it was somewhere between terrible and awful.\"\n\n---\n\nOr this classic:\n\nWhy did the test score go to therapy?\n\nIt had **reliability issues** — it kept giving different answers depending on the day.\n\n---\n\nThese are pretty niche, but if you work in assessment, you feel them deeply. 😄"

A few things to note:

  1. Generally speaking, the process used by a generative AI model involves predicting the next token to be selected and sampling from a distribution of possible options. (We will discuss ways to better control this later.) A (sometimes, mostly) beautiful implication of this is that responses from generative AI models will often be different, even if the exact same prompt is used. This is especially the case in the simple function above, as we haven’t made an effort to tune the model generation parameters to achieve a deterministic or replicable result. Thus, when you call Anthropic and have it generate a joke for you using the same prompt, you will likely get different responses each time. In fact, each time that I compile this book on GitHub the result changes!

  2. You may notice that escape sequences (\n, \t, etc.) may be present because the is returned from the model. One way to handle this is to wrap your response in cat(). We’ll revisit this again later in other sections.

Code
cat(test_joke)
Here's one:

A psychometrician walks into a bar and orders a drink. The bartender asks, "How was your day?"

He says, "Well, I can't be certain, but I'm 95% confident it was somewhere between terrible and awful."

---

Or this classic:

Why did the test score go to therapy?

It had **reliability issues** — it kept giving different answers depending on the day.

---

These are pretty niche, but if you work in assessment, you feel them deeply. 😄