First Request

This guide walks you through making your first request to the ModelRelay API.

Prerequisites

Before you begin, make sure you have:

  1. A ModelRelay account with a project created
  2. A secret API key (mr_sk_*) from your project dashboard

If you haven’t set these up yet, see the Getting Started guide.

Install the SDK

npm install @modelrelay/sdk
go get github.com/modelrelay/sdk-go
# Cargo.toml
[dependencies]
modelrelay = "5.12.0"
tokio = { version = "1", features = ["full"] }

Make a Request

import { ModelRelay } from "@modelrelay/sdk";

const mr = new ModelRelay({
  apiKey: process.env.MODELRELAY_API_KEY,
});

const answer = await mr.responses.text(
  "claude-sonnet-4-5",
  "You are a helpful assistant.",
  "What is the capital of France?"
);

console.log(answer);
// "The capital of France is Paris."
package main

import (
    "context"
    "fmt"
    "log"
    "os"

    sdk "github.com/modelrelay/sdk-go"
)

func main() {
    client, err := sdk.NewClientWithKey(
        sdk.MustParseAPIKey(os.Getenv("MODELRELAY_API_KEY")),
    )
    if err != nil {
        log.Fatal(err)
    }

    answer, err := client.Responses.Text(
        context.Background(),
        sdk.NewModelID("claude-sonnet-4-5"),
        "You are a helpful assistant.",
        "What is the capital of France?",
    )
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(answer)
    // "The capital of France is Paris."
}
use modelrelay::{Client, Config, ResponseBuilder, ApiKey};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::new(Config {
        api_key: Some(ApiKey::parse(&std::env::var("MODELRELAY_API_KEY")?)?),
        ..Default::default()
    })?;

    let answer = ResponseBuilder::text_prompt(
        "You are a helpful assistant.",
        "What is the capital of France?",
    )
    .model("claude-sonnet-4-5")
    .send_text(&client.responses())
    .await?;

    println!("{}", answer);
    // "The capital of France is Paris."

    Ok(())
}

Streaming

For real-time responses, use streaming:

const stream = await mr.responses.streamTextDeltas(
  "claude-sonnet-4-5",
  "You are a helpful assistant.",
  "Write a haiku about programming."
);

for await (const delta of stream) {
  process.stdout.write(delta);
}
stream, err := client.Responses.StreamTextDeltas(
    context.Background(),
    sdk.NewModelID("claude-sonnet-4-5"),
    "You are a helpful assistant.",
    "Write a haiku about programming.",
)
if err != nil {
    log.Fatal(err)
}
defer stream.Close()

for {
    delta, ok, err := stream.Next()
    if err != nil {
        log.Fatal(err)
    }
    if !ok {
        break
    }
    fmt.Print(delta)
}
use futures_util::StreamExt;

let mut stream = ResponseBuilder::text_prompt(
    "You are a helpful assistant.",
    "Write a haiku about programming.",
)
.model("claude-sonnet-4-5")
.stream_deltas(&client.responses())
.await?;

while let Some(delta) = stream.next().await {
    print!("{}", delta?);
}

Full Request Builder

For more control over request parameters:

const response = await mr.responses.create(
  mr.responses
    .new()
    .model("claude-sonnet-4-5")
    .system("You are a helpful assistant.")
    .user("What is 2 + 2?")
    .maxOutputTokens(256)
    .build()
);

console.log(response.output);
// Array of output items (text, tool calls, etc.)

console.log(response.usage);
// { input_tokens: 25, output_tokens: 12 }
req, opts, err := client.Responses.New().
    Model(sdk.NewModelID("claude-sonnet-4-5")).
    System("You are a helpful assistant.").
    User("What is 2 + 2?").
    MaxOutputTokens(256).
    Build()
if err != nil {
    log.Fatal(err)
}

response, err := client.Responses.Create(ctx, req, opts...)
if err != nil {
    log.Fatal(err)
}

fmt.Println(response.AssistantText())
fmt.Printf("Usage: %+v\n", response.Usage)
let response = ResponseBuilder::new()
    .model("claude-sonnet-4-5")
    .system("You are a helpful assistant.")
    .user("What is 2 + 2?")
    .max_output_tokens(256)
    .send(&client.responses())
    .await?;

println!("{}", response.text());
println!("Usage: {:?}", response.usage);

Using curl

You can also call the API directly:

curl -X POST https://api.modelrelay.ai/api/v1/responses \
  -H "Authorization: Bearer $MODELRELAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5",
    "input": [
      {
        "type": "message",
        "role": "system",
        "content": [{ "type": "text", "text": "You are a helpful assistant." }]
      },
      {
        "type": "message",
        "role": "user",
        "content": [{ "type": "text", "text": "What is the capital of France?" }]
      }
    ]
  }'

Response Format

A successful response looks like:

{
  "id": "resp_abc123",
  "model": "claude-sonnet-4-5",
  "output": [
    {
      "type": "message",
      "role": "assistant",
      "content": [
        {
          "type": "text",
          "text": "The capital of France is Paris."
        }
      ]
    }
  ],
  "stop_reason": "end_turn",
  "usage": {
    "input_tokens": 25,
    "output_tokens": 12
  }
}

Response Fields

Field Description
id Unique response identifier
model The model that generated the response
output Array of output items (messages, tool calls)
stop_reason Why generation stopped (end_turn, max_tokens, tool_calls)
usage Token counts for billing

Available Models

ModelRelay supports models from multiple providers. Common options:

Model Provider Description
claude-sonnet-4-5 Anthropic Fast, capable general-purpose model
claude-opus-4-5 Anthropic Most capable Claude model
gpt-5.2 OpenAI OpenAI’s flagship model
gpt-5-mini OpenAI Fast and cost-effective
gemini-2.5-flash-preview-09-2025 Google AI Long context, multimodal
grok-4-1-fast-non-reasoning xAI Fast inference, competitive pricing

Use the Models API to list all available models for your project.

Error Handling

import { ModelRelay, APIError, ConfigError } from "@modelrelay/sdk";

try {
  const answer = await mr.responses.text(
    "claude-sonnet-4-5",
    "You are helpful.",
    "Hello!"
  );
} catch (error) {
  if (error instanceof APIError) {
    console.error(`API error ${error.status}: ${error.message}`);
  } else if (error instanceof ConfigError) {
    console.error(`Config error: ${error.message}`);
  } else {
    throw error;
  }
}
answer, err := client.Responses.Text(ctx, model, system, user)
if err != nil {
    var apiErr *sdk.APIError
    var transportErr sdk.TransportError

    if errors.As(err, &apiErr) {
        log.Printf("API error %d: %s", apiErr.Status, apiErr.Message)
    } else if errors.As(err, &transportErr) {
        log.Printf("Transport error: %s", transportErr.Message)
    } else {
        return err
    }
}
use modelrelay::errors::{Error, APIError};

match ResponseBuilder::text_prompt(system, user)
    .model(model)
    .send_text(&client.responses())
    .await
{
    Ok(answer) => println!("{}", answer),
    Err(Error::API(APIError { status, message, .. })) => {
        eprintln!("API error {}: {}", status, message);
    }
    Err(Error::Transport(e)) => {
        eprintln!("Transport error: {}", e.message);
    }
    Err(e) => return Err(e.into()),
}

Next Steps